Skip to content

Commit 8ccd9ee

Browse files
committed
Copilot test suggestions
1 parent 6d89905 commit 8ccd9ee

2 files changed

Lines changed: 144 additions & 11 deletions

File tree

src/herder/test/HerderTests.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9034,6 +9034,120 @@ TEST_CASE_VERSIONS("Herder properly validates when tx set is missing",
90349034
});
90359035
}
90369036

9037+
// Test that a stalled ballot resumes immediately on tx set arrival
9038+
TEST_CASE_VERSIONS("tx set arrival resumes stalled balloting", "[herder]")
9039+
{
9040+
Config cfg(getTestConfig());
9041+
cfg.MANUAL_CLOSE = false;
9042+
cfg.EXPERIMENTAL_PARALLEL_TX_SET_DOWNLOAD = true;
9043+
9044+
VirtualClock clock;
9045+
9046+
auto v1Key = SecretKey::pseudoRandomForTesting();
9047+
auto v2Key = SecretKey::pseudoRandomForTesting();
9048+
auto const& v1Pk = v1Key.getPublicKey();
9049+
auto const& v2Pk = v2Key.getPublicKey();
9050+
9051+
// Local quorum set {self, v1, v2} with threshold 2
9052+
cfg.QUORUM_SET.threshold = 2;
9053+
cfg.QUORUM_SET.validators.emplace_back(v1Pk);
9054+
cfg.QUORUM_SET.validators.emplace_back(v2Pk);
9055+
9056+
Application::pointer app = createTestApplication(clock, cfg);
9057+
9058+
for_versions_from(
9059+
static_cast<uint32_t>(EMPTY_TX_SET_PROTOCOL_VERSION), *app, [&] {
9060+
auto const lcl =
9061+
app->getLedgerManager().getLastClosedLedgerHeader();
9062+
uint64_t const slotIndex = lcl.header.ledgerSeq + 1;
9063+
auto& herder = dynamic_cast<HerderImpl&>(app->getHerder());
9064+
auto& pendingEnvelopes = herder.getPendingEnvelopes();
9065+
9066+
// Peers use the same 3-node qset; pre-cache it so envelopes don't
9067+
// block on a qset fetch.
9068+
SCPQuorumSet qSet;
9069+
qSet.threshold = 2;
9070+
qSet.validators.push_back(cfg.NODE_SEED.getPublicKey());
9071+
qSet.validators.push_back(v1Pk);
9072+
qSet.validators.push_back(v2Pk);
9073+
auto qSetHash = sha256(xdr::xdr_to_opaque(qSet));
9074+
pendingEnvelopes.addSCPQuorumSet(qSetHash, qSet);
9075+
9076+
// Create a non-empty tx set that the node does not have
9077+
auto root = app->getRoot();
9078+
std::vector<TransactionFrameBasePtr> txs = {
9079+
root->tx({payment(root->getPublicKey(), 1)})};
9080+
auto txSet = makeTxSetFromTransactions(txs, *app, 0, 0).first;
9081+
auto txSetHash = txSet->getContentsHash();
9082+
9083+
auto sv = herder.makeStellarValue(
9084+
txSetHash, lcl.header.scpValue.closeTime + 1,
9085+
emptyUpgradeSteps, v1Key);
9086+
auto opaqueValue = xdr::xdr_to_opaque(sv);
9087+
9088+
auto makePrepareFromPeer = [&](SecretKey const& peerKey) {
9089+
SCPEnvelope env;
9090+
env.statement.slotIndex = slotIndex;
9091+
env.statement.pledges.type(SCP_ST_PREPARE);
9092+
auto& prep = env.statement.pledges.prepare();
9093+
prep.ballot.counter = 1;
9094+
prep.ballot.value = opaqueValue;
9095+
prep.prepared.activate() = prep.ballot;
9096+
prep.quorumSetHash = qSetHash;
9097+
env.statement.nodeID = peerKey.getPublicKey();
9098+
herder.signEnvelope(peerKey, env);
9099+
return env;
9100+
};
9101+
9102+
// Both peers accept-prepared (1, v). The envelopes are
9103+
// ready without the tx set (parallel downloading), and processing
9104+
// them drives the local node to confirm-prepared and then stall
9105+
// because the tx set is still missing.
9106+
REQUIRE(herder.recvSCPEnvelope(makePrepareFromPeer(v1Key)) ==
9107+
Herder::ENVELOPE_STATUS_READY);
9108+
REQUIRE(herder.recvSCPEnvelope(makePrepareFromPeer(v2Key)) ==
9109+
Herder::ENVELOPE_STATUS_READY);
9110+
9111+
auto localPrepare = [&]() {
9112+
auto const* env = herder.getSCP().getLatestMessage(
9113+
cfg.NODE_SEED.getPublicKey());
9114+
REQUIRE(env);
9115+
REQUIRE(env->statement.pledges.type() == SCP_ST_PREPARE);
9116+
return env->statement.pledges.prepare();
9117+
};
9118+
9119+
// Stalled: h is set but the commit is deferred (nC == 0).
9120+
{
9121+
auto const prep = localPrepare();
9122+
REQUIRE(prep.ballot.counter == 1);
9123+
REQUIRE(prep.nH == 1);
9124+
REQUIRE(prep.nC == 0);
9125+
}
9126+
9127+
// Deliver the tx set
9128+
REQUIRE(herder.recvTxSet(txSetHash, txSet));
9129+
9130+
// Resumed: the commit completed at the same counter, indicating
9131+
// the lack of a ballot timeout
9132+
{
9133+
auto const prep = localPrepare();
9134+
REQUIRE(prep.ballot.counter == 1);
9135+
REQUIRE(prep.nH == 1);
9136+
REQUIRE(prep.nC == 1);
9137+
}
9138+
9139+
// Repeat delivery is a no-op: the tx set is no longer being
9140+
// fetched, and balloting state does not change.
9141+
REQUIRE(!herder.recvTxSet(txSetHash, txSet));
9142+
{
9143+
auto const prep = localPrepare();
9144+
REQUIRE(prep.ballot.counter == 1);
9145+
REQUIRE(prep.nH == 1);
9146+
REQUIRE(prep.nC == 1);
9147+
}
9148+
});
9149+
}
9150+
90379151
#ifdef CAP_0083
90389152
// This tests that the network externalizes an empty-tx-set value when a
90399153
// voted-for value is not available on the network.

src/scp/test/SCPTests.cpp

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@ class TestSCP : public SCPDriver
4343
uint32_t mIncrementBallotTimeoutMS = 1000;
4444
uint32_t mInitialNominationTimeoutMS = 1000;
4545
uint32_t mIncrementNominationTimeoutMS = 1000;
46+
bool const mProtocolAllowsEmptyTxSetValues;
4647

4748
TestSCP(NodeID const& nodeID, SCPQuorumSet const& qSetLocal,
48-
bool isValidator = true)
49+
bool isValidator = true, bool protocolAllowsEmptyTxSetValues = true)
4950
: mSCP(*this, nodeID, isValidator, qSetLocal)
51+
, mProtocolAllowsEmptyTxSetValues(protocolAllowsEmptyTxSetValues)
5052
{
5153
mPriorityLookup = [&](NodeID const& n) {
5254
return (n == mSCP.getLocalNodeID()) ? 1000 : 1;
@@ -165,13 +167,17 @@ class TestSCP : public SCPDriver
165167
bool
166168
isParallelTxSetDownloadEnabled() const override
167169
{
168-
return true;
170+
// Leave unimplemented. A node's parallel downloading setting only
171+
// affects higher level systems (such as PendingEnvelopes).
172+
// NominationProtocol and BallotProtocol only reason about whether the
173+
// protocol supports empty-tx-set values
174+
releaseAssert(false);
169175
}
170176

171177
bool
172178
protocolAllowsEmptyTxSetValues() const override
173179
{
174-
return true;
180+
return mProtocolAllowsEmptyTxSetValues;
175181
}
176182

177183
void
@@ -861,7 +867,9 @@ TEST_CASE("ballot protocol core5", "[scp][ballotprotocol]")
861867

862868
uint256 qSetHash = sha256(xdr::xdr_to_opaque(qSet));
863869

864-
TestSCP scp(v0SecretKey.getPublicKey(), qSet);
870+
bool const protocolAllowsEmptyTxSetValues = GENERATE(false, true);
871+
TestSCP scp(v0SecretKey.getPublicKey(), qSet, /*isValidator*/ true,
872+
protocolAllowsEmptyTxSetValues);
865873

866874
auto test = [&](TestSCP& scp) {
867875
scp.storeQuorumSet(std::make_shared<SCPQuorumSet>(qSet));
@@ -2651,7 +2659,8 @@ TEST_CASE("ballot protocol core5", "[scp][ballotprotocol]")
26512659
SECTION("non validator watching the network")
26522660
{
26532661
SIMULATION_CREATE_NODE(NV);
2654-
TestSCP scpNV(vNVSecretKey.getPublicKey(), qSet, false);
2662+
TestSCP scpNV(vNVSecretKey.getPublicKey(), qSet, false,
2663+
protocolAllowsEmptyTxSetValues);
26552664
scpNV.storeQuorumSet(std::make_shared<SCPQuorumSet>(qSet));
26562665
uint256 qSetHashNV = scpNV.mSCP.getLocalNode()->getQuorumSetHash();
26572666

@@ -2680,7 +2689,8 @@ TEST_CASE("ballot protocol core5", "[scp][ballotprotocol]")
26802689

26812690
SECTION("restore ballot protocol")
26822691
{
2683-
TestSCP scp2(v0SecretKey.getPublicKey(), qSet);
2692+
TestSCP scp2(v0SecretKey.getPublicKey(), qSet, /*isValidator*/ true,
2693+
protocolAllowsEmptyTxSetValues);
26842694
scp2.storeQuorumSet(std::make_shared<SCPQuorumSet>(qSet));
26852695
SCPBallot b(2, xValue);
26862696
SECTION("prepare")
@@ -2725,7 +2735,9 @@ TEST_CASE("ballot protocol core3", "[scp][ballotprotocol]")
27252735

27262736
uint256 qSetHash = sha256(xdr::xdr_to_opaque(qSet));
27272737

2728-
TestSCP scp(v0SecretKey.getPublicKey(), qSet);
2738+
bool const protocolAllowsEmptyTxSetValues = GENERATE(false, true);
2739+
TestSCP scp(v0SecretKey.getPublicKey(), qSet, /*isValidator*/ true,
2740+
protocolAllowsEmptyTxSetValues);
27292741

27302742
auto test = [&](TestSCP& scp) {
27312743
scp.storeQuorumSet(std::make_shared<SCPQuorumSet>(qSet));
@@ -2872,7 +2884,9 @@ TEST_CASE("ballot protocol core3", "[scp][ballotprotocol]")
28722884
SECTION("node without self - quorum timeout")
28732885
{
28742886
SIMULATION_CREATE_NODE(NodeNS);
2875-
TestSCP scpNNS(vNodeNSSecretKey.getPublicKey(), qSet);
2887+
TestSCP scpNNS(vNodeNSSecretKey.getPublicKey(), qSet,
2888+
/*isValidator*/ true,
2889+
protocolAllowsEmptyTxSetValues);
28762890
scpNNS.storeQuorumSet(std::make_shared<SCPQuorumSet>(qSet));
28772891
uint256 qSetHashNodeNS =
28782892
scpNNS.mSCP.getLocalNode()->getQuorumSetHash();
@@ -2929,9 +2943,11 @@ TEST_CASE("nomination tests core5", "[scp][nominationprotocol]")
29292943
expectedLeaders.end()));
29302944
};
29312945

2946+
bool const protocolAllowsEmptyTxSetValues = GENERATE(false, true);
29322947
SECTION("nomination - v0 is top")
29332948
{
2934-
TestSCP scp(v0SecretKey.getPublicKey(), qSet);
2949+
TestSCP scp(v0SecretKey.getPublicKey(), qSet, /*isValidator*/ true,
2950+
protocolAllowsEmptyTxSetValues);
29352951

29362952
auto test = [&](TestSCP& scp) {
29372953
uint256 qSetHash0 = scp.mSCP.getLocalNode()->getQuorumSetHash();
@@ -3045,7 +3061,9 @@ TEST_CASE("nomination tests core5", "[scp][nominationprotocol]")
30453061
}
30463062
SECTION("nomination - restored state")
30473063
{
3048-
TestSCP scp2(v0SecretKey.getPublicKey(), qSet);
3064+
TestSCP scp2(v0SecretKey.getPublicKey(), qSet,
3065+
/*isValidator*/ true,
3066+
protocolAllowsEmptyTxSetValues);
30493067
scp2.storeQuorumSet(
30503068
std::make_shared<SCPQuorumSet>(qSet));
30513069

@@ -3282,7 +3300,8 @@ TEST_CASE("nomination tests core5", "[scp][nominationprotocol]")
32823300
}
32833301
SECTION("v1 is top node")
32843302
{
3285-
TestSCP scp(v0SecretKey.getPublicKey(), qSet);
3303+
TestSCP scp(v0SecretKey.getPublicKey(), qSet, /*isValidator*/ true,
3304+
protocolAllowsEmptyTxSetValues);
32863305

32873306
auto test = [&](TestSCP& scp) {
32883307
uint256 qSetHash0 = scp.mSCP.getLocalNode()->getQuorumSetHash();

0 commit comments

Comments
 (0)