Skip to content

Commit 9082f57

Browse files
authored
Enable downloading of transaction sets in parallel with early SCP stages (#5209)
This PR adds support for CAP-0083 and enables downloading of transactions sets in parallel with SCP up until setting `c` during balloting.
2 parents ade8c25 + 6bd15f5 commit 9082f57

46 files changed

Lines changed: 18983 additions & 3967 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

common.mk

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,7 @@ endif # ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION
4646
if CAP_0071
4747
AM_CPPFLAGS += -DCAP_0071
4848
endif
49+
50+
if CAP_0083
51+
AM_CPPFLAGS += -DCAP_0083
52+
endif

configure.ac

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,9 @@ AM_CONDITIONAL(ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION,
564564
AM_CONDITIONAL(CAP_0071,
565565
[test x$enable_next_protocol_version_unsafe_for_production = xyes])
566566

567+
AM_CONDITIONAL(CAP_0083,
568+
[test x$enable_next_protocol_version_unsafe_for_production = xyes])
569+
567570
AC_PATH_PROG(CARGO, cargo)
568571
if test x"$CARGO" = x; then
569572
AC_MSG_ERROR([cannot find cargo, needed for rust code])

docs/metrics.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,16 @@ scp.pending.discarded | counter | number of discarded enve
188188
scp.pending.fetching | counter | number of incomplete envelopes
189189
scp.pending.processed | counter | number of already processed envelopes
190190
scp.pending.ready | counter | number of envelopes ready to process
191+
scp.empty-tx-set.externalized | counter | number of times the local node externalized an empty-tx-set value
192+
scp.empty-tx-set.value-replaced | counter | number of times the ballot protocol swapped a value for an empty-tx-set value
191193
scp.sync.lost | meter | validator lost sync
192194
scp.timeout.nominate | meter | timeouts in nomination
193195
scp.timeout.prepare | meter | timeouts in ballot protocol
194196
scp.timing.nominated | timer | time spent in nomination
195197
scp.timing.externalized | timer | time spent in ballot protocol
196198
scp.timing.first-to-self-externalize-lag | timer | delay between first externalize message and local node externalizing
197199
scp.timing.self-to-others-externalize-lag | timer | delay between local node externalizing and later externalize messages from other nodes
200+
scp.timing.ballot-blocked-on-txset | timer | time balloting was blocked waiting for a txset download (milliseconds)
198201
scp.value.invalid | meter | SCP value is invalid
199202
scp.value.valid | meter | SCP value is valid
200203
scp.slot.values-referenced | histogram | number of values referenced per consensus round

docs/stellar-core_example.cfg

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,12 @@ PEER_TIMEOUT=30
163163
# time when authenticated.
164164
PEER_STRAGGLER_TIMEOUT=120
165165

166+
# TX_SET_DOWNLOAD_TIMEOUT (Integer) default 5000
167+
# Time in milliseconds before a validator gives up waiting on a transaction set
168+
# and votes to drop the tx set from the upcoming ledger. Does nothing without
169+
# `EXPERIMENTAL_PARALLEL_TX_SET_DOWNLOAD` enabled.
170+
TX_SET_DOWNLOAD_TIMEOUT=5000
171+
166172
# MAX_BATCH_WRITE_COUNT (Integer) default 1024
167173
# How many messages can this server send at once to a peer
168174
MAX_BATCH_WRITE_COUNT=1024
@@ -274,6 +280,10 @@ BACKGROUND_OVERLAY_PROCESSING = true
274280
# performance on multicore machines. Note that this is not compatible with SQLite.
275281
EXPERIMENTAL_PARALLEL_LEDGER_APPLY = false
276282

283+
# EXPERIMENTAL_PARALLEL_TX_SET_DOWNLOAD (bool) default false
284+
# Allow downloading of transaction sets in parallel with SCP
285+
EXPERIMENTAL_PARALLEL_TX_SET_DOWNLOAD = false
286+
277287
# BACKGROUND_TX_SIG_VERIFICATION (bool) default true
278288
# Check signatures in the background for transactions received
279289
# over the network. Does nothing if `BACKGROUND_OVERLAY_PROCESSING` is not

src/Makefile.am

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ XDR_FEATURE_FLAGS =
100100
if CAP_0071
101101
XDR_FEATURE_FLAGS += -DCAP_0071
102102
endif
103+
if CAP_0083
104+
XDR_FEATURE_FLAGS += -DCAP_0083
105+
endif
103106

104107
SUFFIXES = .x .h .rs
105108
.x.h:

src/herder/Herder.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,6 @@ uint32 const Herder::SCP_EXTRA_LOOKBACK_LEDGERS = 3u;
2222
std::chrono::minutes const Herder::TX_SET_GC_DELAY(1);
2323
std::chrono::minutes const Herder::CHECK_FOR_DEAD_NODES_MINUTES(15);
2424
uint32 const Herder::FLOW_CONTROL_BYTES_EXTRA_BUFFER(2000);
25+
26+
Hash const Herder::EMPTY_TX_SET_HASH{};
2527
}

src/herder/Herder.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,17 @@
1616
#include <functional>
1717
#include <memory>
1818
#include <string>
19+
#include <variant>
1920

2021
namespace stellar
2122
{
23+
24+
// Returned by getTxSet to distinguish "empty tx set" values (no real tx set)
25+
// from "not yet downloaded" (nullptr).
26+
struct EmptyTxSet
27+
{
28+
};
29+
using TxSetResult = std::variant<TxSetXDRFrameConstPtr, EmptyTxSet>;
2230
class Application;
2331
class XDROutputFileStream;
2432

@@ -79,6 +87,9 @@ class Herder
7987

8088
static std::chrono::minutes const TX_SET_GC_DELAY;
8189

90+
// Hash value indicating a CAP-0083 explicitly empty-tx-set value
91+
static Hash const EMPTY_TX_SET_HASH;
92+
8293
enum State
8394
{
8495
// Starting up, no state is known
@@ -147,7 +158,7 @@ class Herder
147158
#endif
148159
virtual void peerDoesntHave(stellar::MessageType type,
149160
uint256 const& itemID, Peer::pointer peer) = 0;
150-
virtual TxSetXDRFrameConstPtr getTxSet(Hash const& hash) = 0;
161+
virtual TxSetResult getTxSet(Hash const& hash) = 0;
151162
virtual SCPQuorumSetPtr getQSet(Hash const& qSetHash) = 0;
152163

153164
// We are learning about a new envelope.

src/herder/HerderImpl.cpp

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,23 @@ HerderImpl::processExternalized(uint64 slotIndex, StellarValue const& value,
328328
slotIndex, hexAbbrev(value.txSetHash));
329329
}
330330

331-
TxSetXDRFrameConstPtr externalizedSet =
332-
mPendingEnvelopes.getTxSet(value.txSetHash);
331+
auto result = mPendingEnvelopes.getTxSet(value.txSetHash);
332+
TxSetXDRFrameConstPtr externalizedSet;
333+
if (std::holds_alternative<EmptyTxSet>(result))
334+
{
335+
#ifdef CAP_0083
336+
auto const& ov = value.ext.proposedValue();
337+
externalizedSet = TxSetXDRFrame::makeEmpty(ov.previousLedgerHash,
338+
ov.previousLedgerVersion);
339+
#else
340+
releaseAssert(false);
341+
#endif // CAP_0083
342+
}
343+
else
344+
{
345+
externalizedSet = std::get<TxSetXDRFrameConstPtr>(result);
346+
}
347+
releaseAssert(externalizedSet != nullptr);
333348

334349
{
335350
ZoneNamedN(updateSCPHistoryZone, "update SCP history", true);
@@ -937,7 +952,7 @@ HerderImpl::recvSCPEnvelope(SCPEnvelope const& envelope)
937952

938953
Herder::EnvelopeStatus
939954
HerderImpl::recvSCPEnvelope(SCPEnvelope const& envelope,
940-
const SCPQuorumSet& qset,
955+
SCPQuorumSet const& qset,
941956
TxSetXDRFrameConstPtr txset)
942957
{
943958
ZoneScoped;
@@ -1388,7 +1403,7 @@ HerderImpl::peerDoesntHave(MessageType type, uint256 const& itemID,
13881403
mPendingEnvelopes.peerDoesntHave(type, itemID, peer);
13891404
}
13901405

1391-
TxSetXDRFrameConstPtr
1406+
TxSetResult
13921407
HerderImpl::getTxSet(Hash const& hash)
13931408
{
13941409
return mPendingEnvelopes.getTxSet(hash);
@@ -1639,6 +1654,18 @@ HerderImpl::triggerNextLedger(uint32_t ledgerSeqToTrigger,
16391654
return;
16401655
}
16411656

1657+
#ifdef BUILD_TESTS
1658+
if (mApp.getConfig().TESTING_NOMINATE_RANDOM_VALUES &&
1659+
getHerderSCPDriver().protocolAllowsEmptyTxSetValues())
1660+
{
1661+
txSetHash = HashUtils::pseudoRandomForTesting();
1662+
CLOG_INFO(Herder,
1663+
"TESTING_NOMINATE_RANDOM_VALUES: nominating slot {} "
1664+
"with random tx-set hash {}",
1665+
slotIndex, hexAbbrev(txSetHash));
1666+
}
1667+
#endif
1668+
16421669
StellarValue newProposedValue = makeStellarValue(
16431670
txSetHash, nextCloseTime, newUpgrades, mApp.getConfig().NODE_SEED);
16441671
mHerderSCPDriver.nominate(slotIndex, newProposedValue, proposedSet,
@@ -2165,11 +2192,15 @@ HerderImpl::persistSCPState(uint64 slot)
21652192
// saves transaction sets referred by the statement
21662193
for (auto const& h : getValidatedTxSetHashes(e))
21672194
{
2168-
auto txSet = mPendingEnvelopes.getTxSet(h);
2169-
if (txSet && !mApp.getPersistentState().hasTxSet(h))
2195+
auto result = mPendingEnvelopes.getTxSet(h);
2196+
if (auto* txSetPtr = std::get_if<TxSetXDRFrameConstPtr>(&result))
21702197
{
2171-
txSets.insert(std::make_pair(h, txSet));
2198+
if (*txSetPtr && !mApp.getPersistentState().hasTxSet(h))
2199+
{
2200+
txSets.insert(std::make_pair(h, *txSetPtr));
2201+
}
21722202
}
2203+
// EmptyTxSet: nothing to persist
21732204
}
21742205
Hash qsHash = Slot::getCompanionQuorumSetHashFromStatement(e.statement);
21752206
SCPQuorumSetPtr qSet = mPendingEnvelopes.getQSet(qsHash);
@@ -2708,11 +2739,34 @@ bool
27082739
HerderImpl::verifyStellarValueSignature(StellarValue const& sv)
27092740
{
27102741
ZoneScoped;
2711-
auto [b, _] = PubKeyUtils::verifySig(
2712-
sv.ext.lcValueSignature().nodeID, sv.ext.lcValueSignature().signature,
2713-
xdr::xdr_to_opaque(mApp.getNetworkID(), ENVELOPE_TYPE_SCPVALUE,
2714-
sv.txSetHash, sv.closeTime));
2715-
return b;
2742+
switch (sv.ext.v())
2743+
{
2744+
case STELLAR_VALUE_BASIC:
2745+
// This function should never be called with an unsigned value
2746+
releaseAssert(false);
2747+
case STELLAR_VALUE_SIGNED:
2748+
return PubKeyUtils::verifySig(sv.ext.lcValueSignature().nodeID,
2749+
sv.ext.lcValueSignature().signature,
2750+
xdr::xdr_to_opaque(mApp.getNetworkID(),
2751+
ENVELOPE_TYPE_SCPVALUE,
2752+
sv.txSetHash,
2753+
sv.closeTime))
2754+
.valid;
2755+
#ifdef CAP_0083
2756+
case STELLAR_VALUE_EMPTY_TX_SET:
2757+
{
2758+
auto const& ov = sv.ext.proposedValue();
2759+
return PubKeyUtils::verifySig(
2760+
ov.lcValueSignature.nodeID, ov.lcValueSignature.signature,
2761+
xdr::xdr_to_opaque(mApp.getNetworkID(),
2762+
ENVELOPE_TYPE_SCPVALUE, ov.txSetHash,
2763+
sv.closeTime))
2764+
.valid;
2765+
}
2766+
#endif // CAP_0083
2767+
default:
2768+
releaseAssert(false);
2769+
}
27162770
}
27172771

27182772
StellarValue

src/herder/HerderImpl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ class HerderImpl : public Herder
155155
bool recvTxSet(Hash const& hash, TxSetXDRFrameConstPtr txset) override;
156156
void peerDoesntHave(MessageType type, uint256 const& itemID,
157157
Peer::pointer peer) override;
158-
TxSetXDRFrameConstPtr getTxSet(Hash const& hash) override;
158+
TxSetResult getTxSet(Hash const& hash) override;
159159
SCPQuorumSetPtr getQSet(Hash const& qSetHash) override;
160160

161161
// process ready SCP messages. This may trigger the node to externalze new
@@ -234,7 +234,7 @@ class HerderImpl : public Herder
234234
// helper function to sign envelopes
235235
void signEnvelope(SecretKey const& s, SCPEnvelope& envelope);
236236

237-
// helper function to verify SCPValues are signed
237+
// helper function to verify SCPValues signatures
238238
bool verifyStellarValueSignature(StellarValue const& sv);
239239

240240
size_t getMaxQueueSizeOps() const override;

0 commit comments

Comments
 (0)