Skip to content

Commit

Permalink
Filter out non LEDGER_ENTRYs in captive core
Browse files Browse the repository at this point in the history
  • Loading branch information
sisuresh authored and MonsieurNicolas committed May 9, 2022
1 parent 9d0704e commit 2b38097
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 2 deletions.
73 changes: 71 additions & 2 deletions src/ledger/InMemoryLedgerTxn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<EntryIterator::AbstractImpl>
InMemoryLedgerTxn::FilteredEntryIteratorImpl::clone() const
{
return std::make_unique<FilteredEntryIteratorImpl>(mIter);
}

InMemoryLedgerTxn::InMemoryLedgerTxn(InMemoryLedgerTxnRoot& parent,
Database& db)
: LedgerTxn(parent), mDb(db)
Expand Down Expand Up @@ -84,6 +144,14 @@ InMemoryLedgerTxn::updateLedgerKeyMap(EntryIterator iter)
}
}

EntryIterator
InMemoryLedgerTxn::getFilteredEntryIterator(EntryIterator const& iter)
{
auto filteredIterImpl =
std::make_unique<InMemoryLedgerTxn::FilteredEntryIteratorImpl>(iter);
return EntryIterator(std::move(filteredIterImpl));
}

void
InMemoryLedgerTxn::commitChild(EntryIterator iter,
LedgerTxnConsistency cons) noexcept
Expand All @@ -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();
}
Expand Down
24 changes: 24 additions & 0 deletions src/ledger/InMemoryLedgerTxn.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<EntryIterator::AbstractImpl> clone() const override;
};

EntryIterator getFilteredEntryIterator(EntryIterator const& iter);

public:
InMemoryLedgerTxn(InMemoryLedgerTxnRoot& parent, Database& db);
virtual ~InMemoryLedgerTxn();
Expand Down
92 changes: 92 additions & 0 deletions src/ledger/test/LedgerTxnTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
}
}

0 comments on commit 2b38097

Please sign in to comment.