Line data Source code
1 : // Copyright (c) 2025 The Dash Core developers 2 : // Distributed under the MIT software license, see the accompanying 3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 : 5 : #include <test/util/llmq_tests.h> 6 : #include <test/util/setup_common.h> 7 : 8 : #include <consensus/params.h> 9 : #include <llmq/params.h> 10 : #include <llmq/utils.h> 11 : #include <netaddress.h> 12 : #include <random.h> 13 : 14 : #include <boost/test/unit_test.hpp> 15 : 16 : #include <map> 17 : #include <set> 18 : 19 : using namespace llmq; 20 : using namespace llmq::testutils; 21 : 22 146 : BOOST_FIXTURE_TEST_SUITE(llmq_utils_tests, BasicTestingSetup) 23 : 24 149 : BOOST_AUTO_TEST_CASE(trivially_passes) { BOOST_CHECK(true); } 25 : 26 149 : BOOST_AUTO_TEST_CASE(deterministic_outbound_connection_test) 27 : { 28 : // Test deterministic behavior 29 : // DeterministicOutboundConnection returns one of the two input hashes based on a deterministic calculation 30 1 : uint256 proTxHash1 = GetTestQuorumHash(1); 31 1 : uint256 proTxHash2 = GetTestQuorumHash(2); 32 : 33 : // Same inputs should produce same output 34 1 : uint256 conn1a = llmq::utils::DeterministicOutboundConnection(proTxHash1, proTxHash2); 35 1 : uint256 conn1b = llmq::utils::DeterministicOutboundConnection(proTxHash1, proTxHash2); 36 1 : BOOST_CHECK(conn1a == conn1b); 37 : // Result should be one of the input hashes 38 1 : BOOST_CHECK(conn1a == proTxHash1 || conn1a == proTxHash2); 39 : 40 : // Swapped inputs should produce the same result (commutative) 41 : // The function deterministically selects which node initiates the connection 42 1 : uint256 conn2 = llmq::utils::DeterministicOutboundConnection(proTxHash2, proTxHash1); 43 1 : BOOST_CHECK(conn1a == conn2); 44 : 45 : // The result should consistently be the same node regardless of order 46 1 : BOOST_CHECK(llmq::utils::DeterministicOutboundConnection(proTxHash1, proTxHash2) == 47 : llmq::utils::DeterministicOutboundConnection(proTxHash2, proTxHash1)); 48 1 : } 49 : 50 149 : BOOST_AUTO_TEST_CASE(deterministic_outbound_connection_edge_cases_test) 51 : { 52 : // Test with null hashes 53 1 : uint256 nullHash; 54 1 : uint256 validHash = GetTestQuorumHash(1); 55 : 56 : // DeterministicOutboundConnection returns one of the input hashes 57 1 : uint256 conn1 = llmq::utils::DeterministicOutboundConnection(nullHash, validHash); 58 1 : uint256 conn2 = llmq::utils::DeterministicOutboundConnection(validHash, nullHash); 59 1 : uint256 conn3 = llmq::utils::DeterministicOutboundConnection(nullHash, nullHash); 60 : 61 : // With null and valid hash, should return one of them 62 1 : BOOST_CHECK(conn1 == nullHash || conn1 == validHash); 63 1 : BOOST_CHECK(conn2 == nullHash || conn2 == validHash); 64 : // Since the function is order-independent, conn1 and conn2 should be the same 65 1 : BOOST_CHECK(conn1 == conn2); 66 : 67 : // With two null hashes, should return null 68 1 : BOOST_CHECK(conn3 == nullHash); 69 : 70 : // Test with same source and destination 71 1 : uint256 sameHash = GetTestQuorumHash(42); 72 1 : uint256 connSame = llmq::utils::DeterministicOutboundConnection(sameHash, sameHash); 73 : // Should return the same hash 74 1 : BOOST_CHECK(connSame == sameHash); 75 1 : BOOST_CHECK(!connSame.IsNull()); 76 1 : } 77 : 78 : // Note: CalcDeterministicWatchConnections requires CBlockIndex which is complex to mock 79 : // Testing is deferred to functional tests 80 : 81 : // Note: InitQuorumsCache requires specific cache types with LLMQ consensus parameters 82 : // Testing is deferred to integration tests 83 : 84 149 : BOOST_AUTO_TEST_CASE(deterministic_connection_symmetry_test) 85 : { 86 : // Test interesting properties of DeterministicOutboundConnection 87 1 : uint256 proTxHash1 = GetTestQuorumHash(1); 88 1 : uint256 proTxHash2 = GetTestQuorumHash(2); 89 1 : uint256 proTxHash3 = GetTestQuorumHash(3); 90 : 91 : // Create a "network" of connections 92 : // DeterministicOutboundConnection is symmetric - order doesn't matter 93 1 : uint256 conn12 = llmq::utils::DeterministicOutboundConnection(proTxHash1, proTxHash2); 94 1 : uint256 conn21 = llmq::utils::DeterministicOutboundConnection(proTxHash2, proTxHash1); 95 1 : uint256 conn13 = llmq::utils::DeterministicOutboundConnection(proTxHash1, proTxHash3); 96 1 : uint256 conn31 = llmq::utils::DeterministicOutboundConnection(proTxHash3, proTxHash1); 97 1 : uint256 conn23 = llmq::utils::DeterministicOutboundConnection(proTxHash2, proTxHash3); 98 1 : uint256 conn32 = llmq::utils::DeterministicOutboundConnection(proTxHash3, proTxHash2); 99 : 100 : // Verify symmetry - swapped inputs produce same output 101 1 : BOOST_CHECK(conn12 == conn21); 102 1 : BOOST_CHECK(conn13 == conn31); 103 1 : BOOST_CHECK(conn23 == conn32); 104 : 105 : // Each connection returns one of the two nodes 106 1 : BOOST_CHECK(conn12 == proTxHash1 || conn12 == proTxHash2); 107 1 : BOOST_CHECK(conn13 == proTxHash1 || conn13 == proTxHash3); 108 1 : BOOST_CHECK(conn23 == proTxHash2 || conn23 == proTxHash3); 109 : 110 : // The function deterministically picks which node initiates the connection 111 : // Verify we get consistent results for each pair 112 1 : std::set<uint256> uniqueResults; 113 1 : uniqueResults.insert(conn12); 114 1 : uniqueResults.insert(conn13); 115 1 : uniqueResults.insert(conn23); 116 : // Each pair should produce one of its members, but pairs may have overlapping results 117 1 : BOOST_CHECK(uniqueResults.size() >= 2 && uniqueResults.size() <= 3); 118 1 : } 119 : 120 146 : BOOST_AUTO_TEST_SUITE_END()