@@ -5803,3 +5803,155 @@ TEST_CASE("exclude transactions by operation type", "[herder]")
5803
5803
TransactionQueue::AddResult::ADD_STATUS_PENDING);
5804
5804
}
5805
5805
}
5806
+
5807
+ // Test that Herder updates the scphistory table with additional messages from
5808
+ // ledger `n-1` when closing ledger `n`
5809
+ TEST_CASE (" SCP message capture from previous ledger" , " [herder]" )
5810
+ {
5811
+ constexpr uint32_t version =
5812
+ static_cast <uint32_t >(SOROBAN_PROTOCOL_VERSION);
5813
+
5814
+ // Initialize simulation
5815
+ auto networkID = sha256 (getTestConfig ().NETWORK_PASSPHRASE );
5816
+ auto simulation = std::make_shared<Simulation>(
5817
+ Simulation::OVER_LOOPBACK, networkID, [](int i) {
5818
+ auto cfg = getTestConfig (i, Config::TESTDB_ON_DISK_SQLITE);
5819
+ cfg.TESTING_UPGRADE_LEDGER_PROTOCOL_VERSION = version;
5820
+ return cfg;
5821
+ });
5822
+
5823
+ // Create three validators: A, B, and C
5824
+ auto validatorAKey = SecretKey::fromSeed (sha256 (" validator-A" ));
5825
+ auto validatorBKey = SecretKey::fromSeed (sha256 (" validator-B" ));
5826
+ auto validatorCKey = SecretKey::fromSeed (sha256 (" validator-C" ));
5827
+
5828
+ // Put all validators in a quorum set of threshold 2
5829
+ SCPQuorumSet qset;
5830
+ qset.threshold = 2 ;
5831
+ qset.validators .push_back (validatorAKey.getPublicKey ());
5832
+ qset.validators .push_back (validatorBKey.getPublicKey ());
5833
+ qset.validators .push_back (validatorCKey.getPublicKey ());
5834
+
5835
+ // Connect validators A and B, but leave C disconnected
5836
+ auto A = simulation->addNode (validatorAKey, qset);
5837
+ auto B = simulation->addNode (validatorBKey, qset);
5838
+ auto C = simulation->addNode (validatorCKey, qset);
5839
+ simulation->addPendingConnection (validatorAKey.getPublicKey (),
5840
+ validatorBKey.getPublicKey ());
5841
+ simulation->startAllNodes ();
5842
+
5843
+ // Crank A and B until they're on ledger 2
5844
+ simulation->crankUntil (
5845
+ [&]() {
5846
+ return A->getLedgerManager ().getLastClosedLedgerNum () == 2 &&
5847
+ B->getLedgerManager ().getLastClosedLedgerNum () == 2 ;
5848
+ },
5849
+ 4 * Herder::EXP_LEDGER_TIMESPAN_SECONDS, false );
5850
+
5851
+ // Check that a node's scphistory table for a given ledger has the correct
5852
+ // number of entries of each type in `expectedTypes`
5853
+ auto checkSCPHistoryEntries =
5854
+ [&](Application::pointer node, uint32_t ledgerNum,
5855
+ UnorderedMap<SCPStatementType, size_t > const & expectedTypes) {
5856
+ // Prepare query
5857
+ auto & db = node->getDatabase ();
5858
+ auto prep = db.getPreparedStatement (
5859
+ " SELECT envelope FROM scphistory WHERE ledgerseq = :l" );
5860
+ auto & st = prep.statement ();
5861
+ st.exchange (soci::use (ledgerNum));
5862
+ std::string envStr;
5863
+ st.exchange (soci::into (envStr));
5864
+ st.define_and_bind ();
5865
+ st.execute (false );
5866
+
5867
+ // Count the number of entries of each type
5868
+ UnorderedMap<SCPStatementType, size_t > actualTypes;
5869
+ while (st.fetch ())
5870
+ {
5871
+ Value v;
5872
+ decoder::decode_b64 (envStr, v);
5873
+ SCPEnvelope env;
5874
+ xdr::xdr_from_opaque (v, env);
5875
+ ++actualTypes[env.statement .pledges .type ()];
5876
+ }
5877
+
5878
+ return actualTypes == expectedTypes;
5879
+ };
5880
+
5881
+ // Expected counts of scphistory entry types for ledger 2
5882
+ UnorderedMap<SCPStatementType, size_t > expConfExt = {
5883
+ {SCPStatementType::SCP_ST_CONFIRM, 1 },
5884
+ {SCPStatementType::SCP_ST_EXTERNALIZE, 1 }};
5885
+ UnorderedMap<SCPStatementType, size_t > exp2Ext = {
5886
+ {SCPStatementType::SCP_ST_EXTERNALIZE, 2 }};
5887
+
5888
+ // Examine scphistory tables for A and B for ledger 2. Either A has 1
5889
+ // CONFIRM and 1 EXTERNALIZE and B has 2 EXTERNALIZEs, or A has 2
5890
+ // EXTERNALIZEs and B has 1 CONFIRM and 1 EXTERNALIZE.
5891
+ REQUIRE ((checkSCPHistoryEntries (A, 2 , expConfExt) &&
5892
+ checkSCPHistoryEntries (B, 2 , exp2Ext)) ^
5893
+ (checkSCPHistoryEntries (A, 2 , exp2Ext) &&
5894
+ checkSCPHistoryEntries (B, 2 , expConfExt)));
5895
+
5896
+ // C has no entries in its scphistory table for ledger 2.
5897
+ REQUIRE (checkSCPHistoryEntries (C, 2 , {}));
5898
+
5899
+ // Get messages from A and B
5900
+ HerderImpl& herderA = dynamic_cast <HerderImpl&>(A->getHerder ());
5901
+ HerderImpl& herderB = dynamic_cast <HerderImpl&>(B->getHerder ());
5902
+ std::vector<SCPEnvelope> AEnvs = herderA.getSCP ().getLatestMessagesSend (2 );
5903
+ std::vector<SCPEnvelope> BEnvs = herderB.getSCP ().getLatestMessagesSend (2 );
5904
+
5905
+ // Pass A and B's messages to C
5906
+ for (auto const & env : AEnvs)
5907
+ {
5908
+ C->getHerder ().recvSCPEnvelope (env);
5909
+ }
5910
+ for (auto const & env : BEnvs)
5911
+ {
5912
+ C->getHerder ().recvSCPEnvelope (env);
5913
+ }
5914
+
5915
+ // Crank C until it is on ledger 2
5916
+ simulation->crankUntil (
5917
+ [&]() { return C->getLedgerManager ().getLastClosedLedgerNum () == 2 ; },
5918
+ 4 * Herder::EXP_LEDGER_TIMESPAN_SECONDS, false );
5919
+
5920
+ // Get messages from C
5921
+ HerderImpl& herderC = dynamic_cast <HerderImpl&>(C->getHerder ());
5922
+ std::vector<SCPEnvelope> CEnvs = herderC.getSCP ().getLatestMessagesSend (2 );
5923
+
5924
+ // Pass C's messages to A and B
5925
+ for (auto const & env : CEnvs)
5926
+ {
5927
+ A->getHerder ().recvSCPEnvelope (env);
5928
+ B->getHerder ().recvSCPEnvelope (env);
5929
+ }
5930
+
5931
+ // Crank A and B until they're on ledger 3
5932
+ simulation->crankUntil (
5933
+ [&]() {
5934
+ return A->getLedgerManager ().getLastClosedLedgerNum () == 3 &&
5935
+ B->getLedgerManager ().getLastClosedLedgerNum () == 3 ;
5936
+ },
5937
+ 4 * Herder::EXP_LEDGER_TIMESPAN_SECONDS, false );
5938
+
5939
+ // A and B should now each have 3 EXTERNALIZEs in their scphistory table for
5940
+ // ledger 2. A's CONFIRM entry has been replaced with an EXTERNALIZE.
5941
+ UnorderedMap<SCPStatementType, size_t > const expectedTypes = {
5942
+ {SCPStatementType::SCP_ST_EXTERNALIZE, 3 }};
5943
+ REQUIRE (checkSCPHistoryEntries (A, 2 , expectedTypes));
5944
+ REQUIRE (checkSCPHistoryEntries (B, 2 , expectedTypes));
5945
+
5946
+ // Connect C to B and crank C to catch up with A and B
5947
+ simulation->addConnection (validatorCKey.getPublicKey (),
5948
+ validatorBKey.getPublicKey ());
5949
+ simulation->crankUntil (
5950
+ [&]() { return C->getLedgerManager ().getLastClosedLedgerNum () == 3 ; },
5951
+ 4 * Herder::EXP_LEDGER_TIMESPAN_SECONDS, false );
5952
+
5953
+ // C should have 3 EXTERNALIZEs in its scphistory table for ledger 2. This
5954
+ // check ensures that C does not double count messages from ledger 2 when
5955
+ // closing ledger 3.
5956
+ REQUIRE (checkSCPHistoryEntries (C, 2 , expectedTypes));
5957
+ }
5 commit comments
latobarita commentedon Feb 13, 2024
merging bboston7/stellar-core/scp-msgs = 26bba35 into auto
latobarita commentedon Feb 13, 2024
saw approval from marta-lokhova
at bboston7@26bba35
latobarita commentedon Feb 13, 2024
bboston7/stellar-core/scp-msgs = 26bba35 merged ok, testing candidate = d2e6be1
latobarita commentedon Feb 13, 2024
all tests pass:
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21504306250
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503991357
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503991249
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503991128
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503991064
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503990984
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503990902
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503990833
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503990751
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503990670
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503990598
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503990536
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503990428
success: https://github.com/stellar/stellar-core/actions/runs/7881029422/job/21503990255
latobarita commentedon Feb 13, 2024
fast-forwarding master to auto = d2e6be1