From 2b38097cbe980b0b602db023956a37460d5bd29d Mon Sep 17 00:00:00 2001 From: Siddharth Suresh Date: Mon, 9 May 2022 13:10:25 -0700 Subject: [PATCH] Filter out non LEDGER_ENTRYs in captive core --- src/ledger/InMemoryLedgerTxn.cpp | 73 +++++++++++++++++++++++- src/ledger/InMemoryLedgerTxn.h | 24 ++++++++ src/ledger/test/LedgerTxnTests.cpp | 92 ++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 2 deletions(-) diff --git a/src/ledger/InMemoryLedgerTxn.cpp b/src/ledger/InMemoryLedgerTxn.cpp index 28afae787f..bcdaca07a2 100644 --- a/src/ledger/InMemoryLedgerTxn.cpp +++ b/src/ledger/InMemoryLedgerTxn.cpp @@ -12,6 +12,66 @@ namespace stellar { +// Implementation of InMemoryLedgerTxn::FilteredEntryIteratorImpl +// The LedgerTxnRoot backed LedgerTxn commit filters out non LEDGER_ENTRYs at +// the top level in LedgerTxnRoot. This iterator imitates that behavior by +// skipping the same entries. +InMemoryLedgerTxn::FilteredEntryIteratorImpl::FilteredEntryIteratorImpl( + EntryIterator const& begin) + : mIter(begin) +{ + if (mIter && mIter.key().type() != InternalLedgerEntryType::LEDGER_ENTRY) + { + advance(); + } +} + +void +InMemoryLedgerTxn::FilteredEntryIteratorImpl::advance() +{ + while (++mIter && + mIter.key().type() != InternalLedgerEntryType::LEDGER_ENTRY) + { + // Do nothing + } +} + +bool +InMemoryLedgerTxn::FilteredEntryIteratorImpl::atEnd() const +{ + return !mIter; +} + +InternalLedgerEntry const& +InMemoryLedgerTxn::FilteredEntryIteratorImpl::entry() const +{ + return mIter.entry(); +} + +LedgerEntryPtr const& +InMemoryLedgerTxn::FilteredEntryIteratorImpl::entryPtr() const +{ + return mIter.entryPtr(); +} + +bool +InMemoryLedgerTxn::FilteredEntryIteratorImpl::entryExists() const +{ + return mIter.entryExists(); +} + +InternalLedgerKey const& +InMemoryLedgerTxn::FilteredEntryIteratorImpl::key() const +{ + return mIter.key(); +} + +std::unique_ptr +InMemoryLedgerTxn::FilteredEntryIteratorImpl::clone() const +{ + return std::make_unique(mIter); +} + InMemoryLedgerTxn::InMemoryLedgerTxn(InMemoryLedgerTxnRoot& parent, Database& db) : LedgerTxn(parent), mDb(db) @@ -84,6 +144,14 @@ InMemoryLedgerTxn::updateLedgerKeyMap(EntryIterator iter) } } +EntryIterator +InMemoryLedgerTxn::getFilteredEntryIterator(EntryIterator const& iter) +{ + auto filteredIterImpl = + std::make_unique(iter); + return EntryIterator(std::move(filteredIterImpl)); +} + void InMemoryLedgerTxn::commitChild(EntryIterator iter, LedgerTxnConsistency cons) noexcept @@ -94,9 +162,10 @@ InMemoryLedgerTxn::commitChild(EntryIterator iter, } try { - updateLedgerKeyMap(iter); + auto filteredIter = getFilteredEntryIterator(iter); + updateLedgerKeyMap(filteredIter); - LedgerTxn::commitChild(iter, cons); + LedgerTxn::commitChild(filteredIter, cons); mTransaction->commit(); mTransaction.reset(); } diff --git a/src/ledger/InMemoryLedgerTxn.h b/src/ledger/InMemoryLedgerTxn.h index 13b97bff7c..76cf56fcae 100644 --- a/src/ledger/InMemoryLedgerTxn.h +++ b/src/ledger/InMemoryLedgerTxn.h @@ -50,6 +50,30 @@ class InMemoryLedgerTxn : public LedgerTxn void updateLedgerKeyMap(InternalLedgerKey const& genKey, bool add) noexcept; void updateLedgerKeyMap(EntryIterator iter); + class FilteredEntryIteratorImpl : public EntryIterator::AbstractImpl + { + EntryIterator mIter; + + public: + explicit FilteredEntryIteratorImpl(EntryIterator const& begin); + + void advance() override; + + bool atEnd() const override; + + InternalLedgerEntry const& entry() const override; + + LedgerEntryPtr const& entryPtr() const override; + + bool entryExists() const override; + + InternalLedgerKey const& key() const override; + + std::unique_ptr clone() const override; + }; + + EntryIterator getFilteredEntryIterator(EntryIterator const& iter); + public: InMemoryLedgerTxn(InMemoryLedgerTxnRoot& parent, Database& db); virtual ~InMemoryLedgerTxn(); diff --git a/src/ledger/test/LedgerTxnTests.cpp b/src/ledger/test/LedgerTxnTests.cpp index e5b6550e17..46fe2271f8 100644 --- a/src/ledger/test/LedgerTxnTests.cpp +++ b/src/ledger/test/LedgerTxnTests.cpp @@ -4208,3 +4208,95 @@ TEST_CASE("InMemoryLedgerTxn getPoolShareTrustLinesByAccountAndAsset", REQUIRE(kv.first.trustLine().accountID == a1.getPublicKey()); } } + +TEST_CASE_VERSIONS("InMemoryLedgerTxn close multiple ledgers with merges", + "[ledgertxn]") +{ + VirtualClock clock; + Config cfg = getTestConfig(); + cfg.MODE_USES_IN_MEMORY_LEDGER = true; + + auto app = createTestApplication(clock, cfg); + + auto root = TestAccount::createRoot(*app); + auto const& lm = app->getLedgerManager(); + auto a1 = root.create("a1", lm.getLastMinBalance(1)); + auto b1 = root.create("b1", lm.getLastMinBalance(1)); + + for_versions_from(19, *app, [&] { + auto tx1 = txtest::transactionFrameFromOps( + app->getNetworkID(), root, {a1.op(txtest::accountMerge(root))}, + {a1}); + auto tx2 = txtest::transactionFrameFromOps( + app->getNetworkID(), root, {b1.op(txtest::accountMerge(root))}, + {b1}); + txtest::closeLedger(*app, {tx1}); + txtest::closeLedger(*app, {tx2}); + }); +} + +TEST_CASE("InMemoryLedgerTxn filtering", "[ledgertxn]") +{ + VirtualClock clock; + Config cfg = getTestConfig(); + cfg.MODE_USES_IN_MEMORY_LEDGER = true; + + auto app = createTestApplication(clock, cfg); + auto root = TestAccount::createRoot(*app); + auto a1 = root.create("a1", app->getLedgerManager().getLastMinBalance(1)); + + InternalLedgerEntry entry(InternalLedgerEntryType::MAX_SEQ_NUM_TO_APPLY); + entry.maxSeqNumToApplyEntry().sourceAccount = root; + entry.maxSeqNumToApplyEntry().maxSeqNum = 1; + + InternalLedgerEntry entry2(InternalLedgerEntryType::MAX_SEQ_NUM_TO_APPLY); + entry2.maxSeqNumToApplyEntry().sourceAccount = a1; + entry2.maxSeqNumToApplyEntry().maxSeqNum = 1; + + LedgerEntry le = LedgerTestUtils::generateValidLedgerEntry(); + + LedgerTxn ltx(app->getLedgerTxnRoot()); + + SECTION("1 entry") + { + ltx.create(entry); + ltx.commit(); + + // MAX_SEQ_NUM_TO_APPLY was filtered out on commit to InMemoryLedgerTxn + LedgerTxn ltx2(app->getLedgerTxnRoot()); + REQUIRE(!ltx2.load(entry.toKey())); + } + + SECTION("two entries") + { + ltx.create(entry); + ltx.create(entry2); + ltx.commit(); + + LedgerTxn ltx2(app->getLedgerTxnRoot()); + REQUIRE(!ltx2.load(entry.toKey())); + REQUIRE(!ltx2.load(entry2.toKey())); + } + + SECTION("three entries, with one LEDGER_ENTRY") + { + ltx.create(entry); + ltx.create(le); + ltx.create(entry2); + ltx.commit(); + + LedgerTxn ltx1(app->getLedgerTxnRoot()); + REQUIRE(!ltx1.load(entry.toKey())); + REQUIRE(!ltx1.load(entry2.toKey())); + REQUIRE(ltx1.load(LedgerEntryKey(le))); + } + + SECTION("one LEDGER_ENTRY") + { + ltx.create(le); + ltx.commit(); + + LedgerTxn ltx2(app->getLedgerTxnRoot()); + REQUIRE(ltx2.load(LedgerEntryKey(le))); + } +}