Skip to content

Commit e3ecae2

Browse files
committed
Adds HistoryArchiveUtils::readNextEntry<T> to read entries from an XDRInputFileStream with ledgerSeq bounds checking
1 parent 036c3fc commit e3ecae2

File tree

3 files changed

+95
-85
lines changed

3 files changed

+95
-85
lines changed

src/catchup/ApplyCheckpointWork.cpp

Lines changed: 15 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "bucket/LiveBucketList.h"
88
#include "catchup/ApplyLedgerWork.h"
99
#include "history/FileTransferInfo.h"
10+
#include "history/HistoryArchiveUtils.h"
1011
#include "history/HistoryManager.h"
1112
#include "historywork/Progress.h"
1213
#include "ledger/CheckpointRange.h"
@@ -139,37 +140,21 @@ ApplyCheckpointWork::getCurrentTxSet()
139140
auto& lm = mApp.getLedgerManager();
140141
auto seq = lm.getLastClosedLedgerNum() + 1;
141142

142-
// Check mTxHistoryEntry prior to loading next history entry.
143-
// This order is important because it accounts for ledger "gaps"
144-
// in the history archives (which are caused by ledgers with empty tx
145-
// sets, as those are not uploaded).
146-
do
143+
if (HistoryArchiveUtils::readNextEntry<TransactionHistoryEntry>(
144+
mTxHistoryEntry, mTxIn, seq))
147145
{
148-
if (mTxHistoryEntry.ledgerSeq < seq)
146+
releaseAssert(mTxHistoryEntry.ledgerSeq == seq);
147+
CLOG_DEBUG(History, "Loaded txset for ledger {}", seq);
148+
if (mTxHistoryEntry.ext.v() == 0)
149149
{
150-
CLOG_DEBUG(History, "Skipping txset for ledger {}",
151-
mTxHistoryEntry.ledgerSeq);
152-
}
153-
else if (mTxHistoryEntry.ledgerSeq > seq)
154-
{
155-
break;
150+
return TxSetXDRFrame::makeFromWire(mTxHistoryEntry.txSet);
156151
}
157152
else
158153
{
159-
releaseAssert(mTxHistoryEntry.ledgerSeq == seq);
160-
CLOG_DEBUG(History, "Loaded txset for ledger {}", seq);
161-
if (mTxHistoryEntry.ext.v() == 0)
162-
{
163-
return TxSetXDRFrame::makeFromWire(mTxHistoryEntry.txSet);
164-
}
165-
else
166-
{
167-
return TxSetXDRFrame::makeFromWire(
168-
mTxHistoryEntry.ext.generalizedTxSet());
169-
}
154+
return TxSetXDRFrame::makeFromWire(
155+
mTxHistoryEntry.ext.generalizedTxSet());
170156
}
171-
} while (mTxIn && mTxIn.readOne(mTxHistoryEntry));
172-
157+
}
173158
CLOG_DEBUG(History, "Using empty txset for ledger {}", seq);
174159
return TxSetXDRFrame::makeEmpty(lm.getLastClosedLedgerHeader());
175160
}
@@ -181,30 +166,12 @@ ApplyCheckpointWork::getCurrentTxResultSet()
181166
ZoneScoped;
182167
auto& lm = mApp.getLedgerManager();
183168
auto seq = lm.getLastClosedLedgerNum() + 1;
184-
// Check mTxResultSet prior to loading next result set.
185-
// This order is important because it accounts for ledger "gaps"
186-
// in the history archives (which are caused by ledgers with empty tx
187-
// sets, as those are not uploaded).
188-
while (mTxResultIn && mTxResultIn->readOne(*mTxHistoryResultEntry))
169+
if (HistoryArchiveUtils::readNextEntry<TransactionHistoryResultEntry>(
170+
*mTxHistoryResultEntry, *mTxResultIn, seq))
189171
{
190-
if (mTxHistoryResultEntry)
191-
{
192-
if (mTxHistoryResultEntry->ledgerSeq < seq)
193-
{
194-
CLOG_DEBUG(History, "Advancing past txresultset for ledger {}",
195-
mTxHistoryResultEntry->ledgerSeq);
196-
}
197-
else if (mTxHistoryResultEntry->ledgerSeq > seq)
198-
{
199-
break;
200-
}
201-
else
202-
{
203-
releaseAssert(mTxHistoryResultEntry->ledgerSeq == seq);
204-
CLOG_DEBUG(History, "Loaded txresultset for ledger {}", seq);
205-
return std::make_optional(mTxHistoryResultEntry->txResultSet);
206-
}
207-
}
172+
releaseAssert(mTxHistoryResultEntry->ledgerSeq == seq);
173+
CLOG_DEBUG(History, "Loaded txresultset for ledger {}", seq);
174+
return std::make_optional(mTxHistoryResultEntry->txResultSet);
208175
}
209176
CLOG_DEBUG(History, "No txresultset for ledger {}", seq);
210177
return std::nullopt;

src/history/HistoryArchiveUtils.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#pragma once
2+
3+
#include "util/XDRStream.h"
4+
#include <functional>
5+
#include <type_traits>
6+
7+
namespace stellar
8+
{
9+
10+
// Type trait to check for ledgerSeq member.
11+
template<typename T, typename = void>
12+
struct has_ledger_seq : std::false_type {};
13+
template<typename T>
14+
struct has_ledger_seq<T, std::void_t<decltype(std::declval<T>().ledgerSeq)>>
15+
: std::true_type {};
16+
17+
class HistoryArchiveUtils
18+
{
19+
public:
20+
template<typename T>
21+
using ValidationFunc = std::function<bool(T const&)>;
22+
23+
// Read next entry from XDRInputFileStream at targetLedger.
24+
// Returns true if an entry matching targetLedger was found,
25+
// false otherwise.
26+
// Callers must ensure that the stream is open, and reuse the same
27+
// entry object for each call.
28+
// ValidationFunc<T>, if provided, is called on each entry
29+
// to validate it immediately after reading it from the stream.
30+
template<typename T, typename = std::enable_if_t<has_ledger_seq<T>::value>>
31+
static bool readNextEntry(T& entry, XDRInputFileStream& in, uint32_t targetLedger,
32+
std::optional<ValidationFunc<T>> validationFunc = std::nullopt)
33+
{
34+
35+
do
36+
{
37+
if (entry.ledgerSeq < targetLedger)
38+
{
39+
CLOG_DEBUG(History, "Advancing past ledger {} before target ledger {}",
40+
entry.ledgerSeq, targetLedger);
41+
// Skip entries before targetLedger.
42+
continue;
43+
}
44+
else if (entry.ledgerSeq > targetLedger)
45+
{
46+
// No entry for this ledger.
47+
break;
48+
}
49+
else
50+
{
51+
// Found matching entry.
52+
return true;
53+
}
54+
} while (in && in.readOne(entry) && (validationFunc ? (*validationFunc)(entry) : true));
55+
56+
return false;
57+
}
58+
};
59+
60+
} // namespace stellar

src/historywork/VerifyTxResultsWork.cpp

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "historywork/VerifyTxResultsWork.h"
66
#include "history/FileTransferInfo.h"
7+
#include "history/HistoryArchiveUtils.h"
78
#include "ledger/LedgerManager.h"
89
#include "main/ErrorMessages.h"
910
#include "util/FileSystemException.h"
@@ -141,51 +142,33 @@ TransactionHistoryResultEntry
141142
VerifyTxResultsWork::getCurrentTxResultSet(uint32_t ledger)
142143
{
143144
ZoneScoped;
145+
144146
TransactionHistoryResultEntry trs;
145147
trs.ledgerSeq = ledger;
148+
auto validateEntry = [this](TransactionHistoryResultEntry const& entry) {
149+
auto readLedger = entry.ledgerSeq;
150+
auto low = HistoryManager::firstLedgerInCheckpointContaining(
151+
mCheckpoint, mApp.getConfig());
146152

147-
auto readNextWithValidation = [&]() {
148-
auto res = mResIn.readOne(mTxResultEntry);
149-
if (res)
150-
{
151-
auto readLedger = mTxResultEntry.ledgerSeq;
152-
auto low = HistoryManager::firstLedgerInCheckpointContaining(
153-
mCheckpoint, mApp.getConfig());
154-
if (readLedger > mCheckpoint || readLedger < low)
155-
{
156-
throw std::runtime_error("Results outside of checkpoint range");
157-
}
158-
159-
if (readLedger <= mLastSeenLedger)
160-
{
161-
throw std::runtime_error("Malformed or duplicate results: "
162-
"ledgers must be strictly increasing");
163-
}
164-
mLastSeenLedger = readLedger;
165-
}
166-
return res;
167-
};
168-
169-
do
170-
{
171-
if (mTxResultEntry.ledgerSeq < ledger)
172-
{
173-
CLOG_DEBUG(History, "Processed tx results for ledger {}",
174-
mTxResultEntry.ledgerSeq);
175-
}
176-
else if (mTxResultEntry.ledgerSeq > ledger)
153+
if (readLedger > mCheckpoint || readLedger < low)
177154
{
178-
// No tx results in this ledger
179-
break;
155+
throw std::runtime_error("Results outside of checkpoint range");
180156
}
181-
else
157+
if (readLedger <= mLastSeenLedger)
182158
{
183-
CLOG_DEBUG(History, "Loaded tx result set for ledger {}", ledger);
184-
trs.txResultSet = mTxResultEntry.txResultSet;
185-
return trs;
159+
throw std::runtime_error("Malformed or duplicate results: "
160+
"ledgers must be strictly increasing");
186161
}
187-
} while (mResIn && readNextWithValidation());
162+
mLastSeenLedger = readLedger;
163+
return true;
164+
};
188165

166+
if (HistoryArchiveUtils::readNextEntry<TransactionHistoryResultEntry>(
167+
mTxResultEntry, mResIn, ledger, validateEntry))
168+
{
169+
CLOG_DEBUG(History, "Loaded tx result set for ledger {}", ledger);
170+
trs.txResultSet = mTxResultEntry.txResultSet;
171+
}
189172
return trs;
190173
}
191174
}

0 commit comments

Comments
 (0)