diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp index b30dce4756..8fe9451208 100644 --- a/src/test/app/LedgerReplay_test.cpp +++ b/src/test/app/LedgerReplay_test.cpp @@ -23,7 +23,7 @@ namespace test { struct LedgerReplay_test : public beast::unit_test::suite { void - run() override + testReplayLedger() { testcase("Replay ledger"); @@ -46,6 +46,50 @@ struct LedgerReplay_test : public beast::unit_test::suite BEAST_EXPECT(replayed->header().hash == lastClosed->header().hash); } + + void + testReplayBatchLedger() + { + testcase("Replay ledger with batch transactions"); + + using namespace jtx; + + Env env(*this, testable_amendments()); + + auto const alice = Account("alice"); + auto const bob = Account("bob"); + env.fund(XRP(100000), alice, bob); + env.close(); + + auto const seq = env.seq(alice); + auto const batchFee = batch::calcBatchFee(env, 0, 2); + env(batch::outer(alice, seq, batchFee, tfAllOrNothing), + batch::inner(pay(alice, bob, XRP(1)), seq + 1), + batch::inner(pay(alice, bob, XRP(2)), seq + 2), + batch::sig(alice), + ter(tesSUCCESS)); + env.close(); + + LedgerMaster& ledgerMaster = env.app().getLedgerMaster(); + auto const lastClosed = ledgerMaster.getClosedLedger(); + auto const lastClosedParent = + ledgerMaster.getLedgerByHash(lastClosed->header().parentHash); + + auto const replayed = buildLedger( + LedgerReplay(lastClosedParent, lastClosed), + tapNONE, + env.app(), + env.journal); + + BEAST_EXPECT(replayed->header().hash == lastClosed->header().hash); + } + + void + run() override + { + testReplayLedger(); + testReplayBatchLedger(); + } }; enum class InboundLedgersBehavior { diff --git a/src/xrpld/app/ledger/detail/BuildLedger.cpp b/src/xrpld/app/ledger/detail/BuildLedger.cpp index 3b48ab13c5..58c77634fe 100644 --- a/src/xrpld/app/ledger/detail/BuildLedger.cpp +++ b/src/xrpld/app/ledger/detail/BuildLedger.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace xrpl { @@ -215,7 +216,12 @@ buildLedger( j, [&](OpenView& accum, std::shared_ptr const& built) { for (auto& tx : replayData.orderedTxns()) - applyTransaction(app, accum, *tx.second, false, applyFlags, j); + { + if (tx.second->isFlag(tfInnerBatchTxn)) + continue; + applyTransaction( + app, accum, *tx.second, false, applyFlags, j); + } }); }