forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathllmq_utils_tests.cpp
More file actions
200 lines (160 loc) · 8.61 KB
/
Copy pathllmq_utils_tests.cpp
File metadata and controls
200 lines (160 loc) · 8.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Copyright (c) 2025 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/util/llmq_tests.h>
#include <test/util/setup_common.h>
#include <consensus/params.h>
#include <llmq/params.h>
#include <llmq/signing_shares.h>
#include <llmq/utils.h>
#include <netaddress.h>
#include <random.h>
#include <boost/test/unit_test.hpp>
#include <map>
#include <set>
using namespace llmq;
using namespace llmq::testutils;
BOOST_FIXTURE_TEST_SUITE(llmq_utils_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(trivially_passes) { BOOST_CHECK(true); }
static CSigSesAnn MakeSigSesAnn(uint32_t session_id, uint32_t nonce, Consensus::LLMQType llmq_type = Consensus::LLMQType::LLMQ_50_60)
{
return CSigSesAnn{session_id, llmq_type, GetTestQuorumHash(1), GetTestQuorumHash(2), GetTestQuorumHash(nonce)};
}
static CSigShare MakeSigShare(uint32_t nonce, Consensus::LLMQType llmq_type = Consensus::LLMQType::LLMQ_50_60)
{
CSigShare sig_share{llmq_type, GetTestQuorumHash(1), GetTestQuorumHash(2), GetTestQuorumHash(nonce), 1, CBLSLazySignature{}};
sig_share.UpdateKey();
return sig_share;
}
BOOST_AUTO_TEST_CASE(sig_ses_ann_respects_session_limit_but_allows_refresh)
{
CSigSharesNodeState node_state;
const CSigSesAnn ann1{MakeSigSesAnn(1, 1)};
const CSigSesAnn ann2{MakeSigSesAnn(2, 2)};
const CSigSesAnn ann3{MakeSigSesAnn(3, 3)};
constexpr size_t max_sessions{2};
BOOST_CHECK(node_state.CanCreateSessionFromAnn(ann1, max_sessions));
node_state.GetOrCreateSessionFromAnn(ann1);
BOOST_CHECK_EQUAL(node_state.GetSessionCount(), 1U);
BOOST_CHECK_EQUAL(node_state.GetAnnouncementSessionCount(Consensus::LLMQType::LLMQ_50_60), 1U);
BOOST_CHECK(node_state.CanCreateSessionFromAnn(ann2, max_sessions));
node_state.GetOrCreateSessionFromAnn(ann2);
BOOST_CHECK_EQUAL(node_state.GetSessionCount(), max_sessions);
BOOST_CHECK_EQUAL(node_state.GetAnnouncementSessionCount(Consensus::LLMQType::LLMQ_50_60), max_sessions);
BOOST_CHECK(!node_state.CanCreateSessionFromAnn(ann3, max_sessions));
const CSigSesAnn ann1_refresh{4, Consensus::LLMQType::LLMQ_50_60, ann1.getQuorumHash(), ann1.getId(), ann1.getMsgHash()};
BOOST_CHECK(node_state.CanCreateSessionFromAnn(ann1_refresh, max_sessions));
node_state.GetOrCreateSessionFromAnn(ann1_refresh);
BOOST_CHECK_EQUAL(node_state.GetSessionCount(), max_sessions);
BOOST_CHECK_EQUAL(node_state.GetAnnouncementSessionCount(Consensus::LLMQType::LLMQ_50_60), max_sessions);
}
BOOST_AUTO_TEST_CASE(sig_ses_ann_limit_ignores_send_only_sessions)
{
CSigSharesNodeState node_state;
constexpr size_t max_sessions{1};
const CSigShare sig_share{MakeSigShare(1)};
const CSigSesAnn ann{MakeSigSesAnn(1, 2)};
node_state.GetOrCreateSessionFromShare(sig_share);
BOOST_CHECK_EQUAL(node_state.GetSessionCount(Consensus::LLMQType::LLMQ_50_60), 1U);
BOOST_CHECK_EQUAL(node_state.GetAnnouncementSessionCount(Consensus::LLMQType::LLMQ_50_60), 0U);
BOOST_CHECK(node_state.CanCreateSessionFromAnn(ann, max_sessions));
node_state.GetOrCreateSessionFromAnn(ann);
BOOST_CHECK_EQUAL(node_state.GetSessionCount(Consensus::LLMQType::LLMQ_50_60), 2U);
BOOST_CHECK_EQUAL(node_state.GetAnnouncementSessionCount(Consensus::LLMQType::LLMQ_50_60), 1U);
}
BOOST_AUTO_TEST_CASE(sig_ses_ann_limit_is_per_llmq_type)
{
CSigSharesNodeState node_state;
constexpr size_t max_sessions{1};
const CSigSesAnn ann1{MakeSigSesAnn(1, 1)};
const CSigSesAnn ann2{MakeSigSesAnn(2, 2)};
const CSigSesAnn other_type_ann{MakeSigSesAnn(3, 3, Consensus::LLMQType::LLMQ_400_60)};
BOOST_CHECK(node_state.CanCreateSessionFromAnn(ann1, max_sessions));
node_state.GetOrCreateSessionFromAnn(ann1);
BOOST_CHECK_EQUAL(node_state.GetSessionCount(), 1U);
BOOST_CHECK_EQUAL(node_state.GetSessionCount(Consensus::LLMQType::LLMQ_50_60), 1U);
BOOST_CHECK(!node_state.CanCreateSessionFromAnn(ann2, max_sessions));
BOOST_CHECK(node_state.CanCreateSessionFromAnn(other_type_ann, max_sessions));
node_state.GetOrCreateSessionFromAnn(other_type_ann);
BOOST_CHECK_EQUAL(node_state.GetSessionCount(), 2U);
BOOST_CHECK_EQUAL(node_state.GetSessionCount(Consensus::LLMQType::LLMQ_400_60), 1U);
}
BOOST_AUTO_TEST_CASE(deterministic_outbound_connection_test)
{
// Test deterministic behavior
// DeterministicOutboundConnection returns one of the two input hashes based on a deterministic calculation
uint256 proTxHash1 = GetTestQuorumHash(1);
uint256 proTxHash2 = GetTestQuorumHash(2);
// Same inputs should produce same output
uint256 conn1a = llmq::utils::DeterministicOutboundConnection(proTxHash1, proTxHash2);
uint256 conn1b = llmq::utils::DeterministicOutboundConnection(proTxHash1, proTxHash2);
BOOST_CHECK(conn1a == conn1b);
// Result should be one of the input hashes
BOOST_CHECK(conn1a == proTxHash1 || conn1a == proTxHash2);
// Swapped inputs should produce the same result (commutative)
// The function deterministically selects which node initiates the connection
uint256 conn2 = llmq::utils::DeterministicOutboundConnection(proTxHash2, proTxHash1);
BOOST_CHECK(conn1a == conn2);
// The result should consistently be the same node regardless of order
BOOST_CHECK(llmq::utils::DeterministicOutboundConnection(proTxHash1, proTxHash2) ==
llmq::utils::DeterministicOutboundConnection(proTxHash2, proTxHash1));
}
BOOST_AUTO_TEST_CASE(deterministic_outbound_connection_edge_cases_test)
{
// Test with null hashes
uint256 nullHash;
uint256 validHash = GetTestQuorumHash(1);
// DeterministicOutboundConnection returns one of the input hashes
uint256 conn1 = llmq::utils::DeterministicOutboundConnection(nullHash, validHash);
uint256 conn2 = llmq::utils::DeterministicOutboundConnection(validHash, nullHash);
uint256 conn3 = llmq::utils::DeterministicOutboundConnection(nullHash, nullHash);
// With null and valid hash, should return one of them
BOOST_CHECK(conn1 == nullHash || conn1 == validHash);
BOOST_CHECK(conn2 == nullHash || conn2 == validHash);
// Since the function is order-independent, conn1 and conn2 should be the same
BOOST_CHECK(conn1 == conn2);
// With two null hashes, should return null
BOOST_CHECK(conn3 == nullHash);
// Test with same source and destination
uint256 sameHash = GetTestQuorumHash(42);
uint256 connSame = llmq::utils::DeterministicOutboundConnection(sameHash, sameHash);
// Should return the same hash
BOOST_CHECK(connSame == sameHash);
BOOST_CHECK(!connSame.IsNull());
}
// Note: CalcDeterministicWatchConnections requires CBlockIndex which is complex to mock
// Testing is deferred to functional tests
// Note: InitQuorumsCache requires specific cache types with LLMQ consensus parameters
// Testing is deferred to integration tests
BOOST_AUTO_TEST_CASE(deterministic_connection_symmetry_test)
{
// Test interesting properties of DeterministicOutboundConnection
uint256 proTxHash1 = GetTestQuorumHash(1);
uint256 proTxHash2 = GetTestQuorumHash(2);
uint256 proTxHash3 = GetTestQuorumHash(3);
// Create a "network" of connections
// DeterministicOutboundConnection is symmetric - order doesn't matter
uint256 conn12 = llmq::utils::DeterministicOutboundConnection(proTxHash1, proTxHash2);
uint256 conn21 = llmq::utils::DeterministicOutboundConnection(proTxHash2, proTxHash1);
uint256 conn13 = llmq::utils::DeterministicOutboundConnection(proTxHash1, proTxHash3);
uint256 conn31 = llmq::utils::DeterministicOutboundConnection(proTxHash3, proTxHash1);
uint256 conn23 = llmq::utils::DeterministicOutboundConnection(proTxHash2, proTxHash3);
uint256 conn32 = llmq::utils::DeterministicOutboundConnection(proTxHash3, proTxHash2);
// Verify symmetry - swapped inputs produce same output
BOOST_CHECK(conn12 == conn21);
BOOST_CHECK(conn13 == conn31);
BOOST_CHECK(conn23 == conn32);
// Each connection returns one of the two nodes
BOOST_CHECK(conn12 == proTxHash1 || conn12 == proTxHash2);
BOOST_CHECK(conn13 == proTxHash1 || conn13 == proTxHash3);
BOOST_CHECK(conn23 == proTxHash2 || conn23 == proTxHash3);
// The function deterministically picks which node initiates the connection
// Verify we get consistent results for each pair
std::set<uint256> uniqueResults;
uniqueResults.insert(conn12);
uniqueResults.insert(conn13);
uniqueResults.insert(conn23);
// Each pair should produce one of its members, but pairs may have overlapping results
BOOST_CHECK(uniqueResults.size() >= 2 && uniqueResults.size() <= 3);
}
BOOST_AUTO_TEST_SUITE_END()