Line data Source code
1 : // Copyright (c) 2021-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 : #ifndef BITCOIN_LLMQ_PARAMS_H
6 : #define BITCOIN_LLMQ_PARAMS_H
7 :
8 : #include <array>
9 : #include <cstdint>
10 : #include <string_view>
11 :
12 : namespace Consensus {
13 :
14 : enum class LLMQType : uint8_t {
15 : LLMQ_NONE = 0xff,
16 :
17 : LLMQ_50_60 = 1, // 50 members, 30 (60%) threshold, one per hour
18 : LLMQ_400_60 = 2, // 400 members, 240 (60%) threshold, one every 12 hours
19 : LLMQ_400_85 = 3, // 400 members, 340 (85%) threshold, one every 24 hours
20 : LLMQ_100_67 = 4, // 100 members, 67 (67%) threshold, one per hour
21 : LLMQ_60_75 = 5, // 60 members, 45 (75%) threshold, one every 12 hours
22 : LLMQ_25_67 = 6, // 25 members, 17 (67%) threshold, one per hour
23 :
24 : // for testing only
25 : LLMQ_TEST = 100, // 3 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestparams is used
26 :
27 : // for devnets only
28 : LLMQ_DEVNET = 101, // 12 members, 6 (50%) threshold, one per hour. Params might differ when -llmqdevnetparams is used
29 : LLMQ_DEVNET_PLATFORM = 107, // 12 members, 8 (67%) threshold, one per hour.
30 :
31 : // for testing activation of new quorums only
32 : LLMQ_TEST_V17 = 102, // 3 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestparams is used
33 :
34 : // for testing only
35 : LLMQ_TEST_DIP0024 = 103, // 4 members, 3 (75%) threshold, one per hour.
36 : LLMQ_TEST_INSTANTSEND = 104, // 3 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestinstantsendparams is used
37 : LLMQ_TEST_PLATFORM = 106, // 3 members, 2 (66%) threshold, one per hour.
38 :
39 : // for devnets only. rotated version (v2) for devnets
40 : LLMQ_DEVNET_DIP0024 = 105, // 8 members, 4 (50%) threshold, one per hour. Params might differ when -llmqdevnetparams is used
41 : };
42 :
43 : // Configures a LLMQ and its DKG
44 : // See https://github.com/dashpay/dips/blob/master/dip-0006.md for more details
45 : struct LLMQParams {
46 : LLMQType type;
47 :
48 : // not consensus critical, only used in logging, RPC and UI
49 : std::string_view name;
50 :
51 : // Whether this is a DIP0024 quorum or not
52 : bool useRotation;
53 :
54 : // the size of the quorum, e.g. 50 or 400
55 : int size;
56 :
57 : // The minimum number of valid members after the DKG. If less members are determined valid, no commitment can be
58 : // created. Should be higher then the threshold to allow some room for failing nodes, otherwise quorum might end up
59 : // not being able to ever created a recovered signature if more nodes fail after the DKG
60 : int minSize;
61 :
62 : // The threshold required to recover a final signature. Should be at least 50%+1 of the quorum size. This value
63 : // also controls the size of the public key verification vector and has a large influence on the performance of
64 : // recovery. It also influences the amount of minimum messages that need to be exchanged for a single signing session.
65 : // This value has the most influence on the security of the quorum. The number of total malicious masternodes
66 : // required to negatively influence signing sessions highly correlates to the threshold percentage.
67 : int threshold;
68 :
69 : // The interval in number blocks for DKGs and the creation of LLMQs. If set to 24 for example, a DKG will start
70 : // every 24 blocks, which is approximately once every hour.
71 : int dkgInterval;
72 :
73 : // The number of blocks per phase in a DKG session. There are 6 phases plus the mining phase that need to be processed
74 : // per DKG. Set this value to a number of blocks so that each phase has enough time to propagate all required
75 : // messages to all members before the next phase starts. If blocks are produced too fast, whole DKG sessions will
76 : // fail.
77 : int dkgPhaseBlocks;
78 :
79 : // The starting block inside the DKG interval for when mining of commitments starts. The value is inclusive.
80 : // Starting from this block, the inclusion of (possibly null) commitments is enforced until the first non-null
81 : // commitment is mined. The chosen value should be at least 5 * dkgPhaseBlocks so that it starts right after the
82 : // finalization phase.
83 : int dkgMiningWindowStart;
84 :
85 : // The ending block inside the DKG interval for when mining of commitments ends. The value is inclusive.
86 : // Choose a value so that miners have enough time to receive the commitment and mine it. Also take into consideration
87 : // that miners might omit real commitments and revert to always including null commitments. The mining window should
88 : // be large enough so that other miners have a chance to produce a block containing a non-null commitment. The window
89 : // should at the same time not be too large so that not too much space is wasted with null commitments in case a DKG
90 : // session failed.
91 : int dkgMiningWindowEnd;
92 :
93 : // In the complaint phase, members will vote on other members being bad (missing valid contribution). If at least
94 : // dkgBadVotesThreshold have voted for another member to be bad, it will considered to be bad by all other members
95 : // as well. This serves as a protection against late-comers who send their contribution on the bring of
96 : // phase-transition, which would otherwise result in inconsistent views of the valid members set
97 : int dkgBadVotesThreshold;
98 :
99 : // Number of quorums to consider "active" for signing sessions
100 : int signingActiveQuorumCount;
101 :
102 : // Used for intra-quorum communication. This is the number of quorums for which we should keep old connections.
103 : // For non-rotated quorums it should be at least one more than the active quorums set.
104 : // For rotated quorums it should be equal to 2 x active quorums set.
105 : int keepOldConnections;
106 :
107 : // The number of quorums for which we should keep keys. Usually it's equal to signingActiveQuorumCount * 2.
108 : // Unlike for other quorum types we want to keep data (secret key shares and vvec)
109 : // for Platform quorums for much longer because Platform can be restarted and
110 : // it must be able to re-sign stuff.
111 :
112 : int keepOldKeys;
113 :
114 : // How many members should we try to send all sigShares to before we give up.
115 : int recoveryMembers;
116 : public:
117 30474 : [[nodiscard]] constexpr int max_cycles(int quorums_count) const
118 : {
119 30474 : return useRotation ? quorums_count / signingActiveQuorumCount : quorums_count;
120 : }
121 :
122 : // For how many blocks recent DKG info should be kept
123 26636 : [[nodiscard]] constexpr int max_store_depth() const { return max_cycles(keepOldKeys) * dkgInterval; }
124 67167 : [[nodiscard]] constexpr bool is_single_member() const { return size == 1; }
125 : };
126 :
127 : //static_assert(std::is_trivial_v<Consensus::LLMQParams>, "LLMQParams is not a trivial type");
128 : static_assert(std::is_trivially_copyable_v<Consensus::LLMQParams>, "LLMQParams is not trivially copyable");
129 : //static_assert(std::is_trivially_default_constructible_v<Consensus::LLMQParams>, "LLMQParams is not trivially default constructible");
130 : static_assert(std::is_trivially_assignable_v<Consensus::LLMQParams, Consensus::LLMQParams>, "LLMQParams is not trivially assignable");
131 :
132 :
133 : static constexpr std::array<LLMQParams, 14> available_llmqs = {
134 :
135 : /**
136 : * llmq_test
137 : * This quorum is only used for testing
138 : *
139 : */
140 : LLMQParams{
141 : .type = LLMQType::LLMQ_TEST,
142 : .name = "llmq_test",
143 : .useRotation = false,
144 : .size = 3,
145 : .minSize = 2,
146 : .threshold = 2,
147 :
148 : .dkgInterval = 24, // one DKG per hour
149 : .dkgPhaseBlocks = 2,
150 : .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
151 : .dkgMiningWindowEnd = 18,
152 : .dkgBadVotesThreshold = 2,
153 :
154 : .signingActiveQuorumCount = 2, // just a few ones to allow easier testing
155 :
156 : .keepOldConnections = 3,
157 : .keepOldKeys = 4,
158 : .recoveryMembers = 3,
159 : },
160 :
161 : /**
162 : * llmq_test_instantsend (same as llmq_test but used for InstantSend exclusively)
163 : * This quorum is only used for testing
164 : *
165 : */
166 : LLMQParams{
167 : .type = LLMQType::LLMQ_TEST_INSTANTSEND,
168 : .name = "llmq_test_instantsend",
169 : .useRotation = false,
170 : .size = 3,
171 : .minSize = 2,
172 : .threshold = 2,
173 :
174 : .dkgInterval = 24, // one DKG per hour
175 : .dkgPhaseBlocks = 2,
176 : .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
177 : .dkgMiningWindowEnd = 18,
178 : .dkgBadVotesThreshold = 2,
179 :
180 : .signingActiveQuorumCount = 2, // just a few ones to allow easier testing
181 :
182 : .keepOldConnections = 3,
183 : .keepOldKeys = 4,
184 : .recoveryMembers = 3,
185 : },
186 :
187 : /**
188 : * llmq_test (Dash Core 0.17) aka llmq_test_v17
189 : * This quorum is only used for testing
190 : *
191 : */
192 : LLMQParams{
193 : .type = LLMQType::LLMQ_TEST_V17,
194 : .name = "llmq_test_v17",
195 : .useRotation = false,
196 : .size = 3,
197 : .minSize = 2,
198 : .threshold = 2,
199 :
200 : .dkgInterval = 24, // one DKG per hour
201 : .dkgPhaseBlocks = 2,
202 : .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
203 : .dkgMiningWindowEnd = 18,
204 : .dkgBadVotesThreshold = 2,
205 :
206 : .signingActiveQuorumCount = 2, // just a few ones to allow easier testing
207 :
208 : .keepOldConnections = 3,
209 : .keepOldKeys = 4,
210 : .recoveryMembers = 3,
211 : },
212 :
213 : /**
214 : * llmq_test_dip0024
215 : * This quorum is only used for testing
216 : *
217 : */
218 : LLMQParams{
219 : .type = LLMQType::LLMQ_TEST_DIP0024,
220 : .name = "llmq_test_dip0024",
221 : .useRotation = true,
222 : .size = 4,
223 : .minSize = 4,
224 : .threshold = 3,
225 :
226 : .dkgInterval = 24, // DKG cycle
227 : .dkgPhaseBlocks = 2,
228 : .dkgMiningWindowStart = 12, // signingActiveQuorumCount + dkgPhaseBlocks * 5 = after finalization
229 : .dkgMiningWindowEnd = 20,
230 : .dkgBadVotesThreshold = 2,
231 :
232 : .signingActiveQuorumCount = 2, // just a few ones to allow easier testing
233 :
234 : .keepOldConnections = 4,
235 : .keepOldKeys = 4,
236 : .recoveryMembers = 3,
237 : },
238 :
239 : /**
240 : * llmq_test_platform
241 : * This quorum is only used for testing
242 : *
243 : */
244 : LLMQParams{
245 : .type = LLMQType::LLMQ_TEST_PLATFORM,
246 : .name = "llmq_test_platform",
247 : .useRotation = false,
248 : .size = 3,
249 : .minSize = 2,
250 : .threshold = 2,
251 :
252 : .dkgInterval = 24, // DKG cycle
253 : .dkgPhaseBlocks = 2,
254 : .dkgMiningWindowStart = 10, // signingActiveQuorumCount + dkgPhaseBlocks * 5 = after finalization
255 : .dkgMiningWindowEnd = 18,
256 : .dkgBadVotesThreshold = 2,
257 :
258 : .signingActiveQuorumCount = 2, // just a few ones to allow easier testing
259 :
260 : .keepOldConnections = 4,
261 : .keepOldKeys = 24 * 30 * 2, // 2 months of quorums
262 : .recoveryMembers = 3,
263 : },
264 :
265 : /**
266 : * llmq_devnet
267 : * This quorum is only used for testing on devnets
268 : *
269 : */
270 : LLMQParams{
271 : .type = LLMQType::LLMQ_DEVNET,
272 : .name = "llmq_devnet",
273 : .useRotation = false,
274 : .size = 12,
275 : .minSize = 7,
276 : .threshold = 6,
277 :
278 : .dkgInterval = 24, // one DKG per hour
279 : .dkgPhaseBlocks = 2,
280 : .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
281 : .dkgMiningWindowEnd = 18,
282 : .dkgBadVotesThreshold = 7,
283 :
284 : .signingActiveQuorumCount = 4, // just a few ones to allow easier testing
285 :
286 : .keepOldConnections = 5,
287 : .keepOldKeys = 8,
288 : .recoveryMembers = 6,
289 : },
290 :
291 : /**
292 : * llmq_devnet_dip0024
293 : * This quorum is only used for testing on devnets
294 : *
295 : */
296 : LLMQParams{
297 : .type = LLMQType::LLMQ_DEVNET_DIP0024,
298 : .name = "llmq_devnet_dip0024",
299 : .useRotation = true,
300 : .size = 8,
301 : .minSize = 6,
302 : .threshold = 4,
303 :
304 : .dkgInterval = 48, // DKG cycle
305 : .dkgPhaseBlocks = 2,
306 : .dkgMiningWindowStart = 12, // signingActiveQuorumCount + dkgPhaseBlocks * 5 = after finalization
307 : .dkgMiningWindowEnd = 20,
308 : .dkgBadVotesThreshold = 7,
309 :
310 : .signingActiveQuorumCount = 2, // just a few ones to allow easier testing
311 :
312 : .keepOldConnections = 4,
313 : .keepOldKeys = 4,
314 : .recoveryMembers = 4,
315 : },
316 :
317 : /**
318 : * llmq_devnet_platform
319 : * This quorum is only used for testing on devnets
320 : *
321 : */
322 : LLMQParams{
323 : .type = LLMQType::LLMQ_DEVNET_PLATFORM,
324 : .name = "llmq_devnet_platform",
325 : .useRotation = false,
326 : .size = 12,
327 : .minSize = 9,
328 : .threshold = 8,
329 :
330 : .dkgInterval = 24, // one DKG per hour
331 : .dkgPhaseBlocks = 2,
332 : .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
333 : .dkgMiningWindowEnd = 18,
334 : .dkgBadVotesThreshold = 7,
335 :
336 : .signingActiveQuorumCount = 4, // just a few ones to allow easier testing
337 :
338 : .keepOldConnections = 5,
339 : .keepOldKeys = 24 * 30 * 2, // 2 months of quorums
340 : .recoveryMembers = 6,
341 : },
342 :
343 : /**
344 : * llmq_50_60
345 : * This quorum is deployed on mainnet and requires
346 : * 40 - 50 participants
347 : *
348 : */
349 : LLMQParams{
350 : .type = LLMQType::LLMQ_50_60,
351 : .name = "llmq_50_60",
352 : .useRotation = false,
353 : .size = 50,
354 : .minSize = 40,
355 : .threshold = 30,
356 :
357 : .dkgInterval = 24, // one DKG per hour
358 : .dkgPhaseBlocks = 2,
359 : .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
360 : .dkgMiningWindowEnd = 18,
361 : .dkgBadVotesThreshold = 40,
362 :
363 : .signingActiveQuorumCount = 24, // a full day worth of LLMQs
364 : .keepOldConnections = 25,
365 : .keepOldKeys = 48,
366 : .recoveryMembers = 25,
367 : },
368 :
369 : /**
370 : * llmq_60_75
371 : * This quorum is deployed on mainnet and requires
372 : * 50 - 60 participants
373 : *
374 : */
375 : LLMQParams{
376 : .type = LLMQType::LLMQ_60_75,
377 : .name = "llmq_60_75",
378 : .useRotation = true,
379 : .size = 60,
380 : .minSize = 50,
381 : .threshold = 45,
382 :
383 : .dkgInterval = 24 * 12, // DKG cycle every 12 hours
384 : .dkgPhaseBlocks = 2,
385 : .dkgMiningWindowStart = 42, // signingActiveQuorumCount + dkgPhaseBlocks * 5 = after finalization
386 : .dkgMiningWindowEnd = 50,
387 : .dkgBadVotesThreshold = 48,
388 :
389 : .signingActiveQuorumCount = 32,
390 : .keepOldConnections = 64,
391 : .keepOldKeys = 64,
392 : .recoveryMembers = 25,
393 : },
394 :
395 : /**
396 : * llmq_400_60
397 : * This quorum is deployed on mainnet and requires
398 : * 300 - 400 participants
399 : *
400 : */
401 : LLMQParams{
402 : .type = LLMQType::LLMQ_400_60,
403 : .name = "llmq_400_60",
404 : .useRotation = false,
405 : .size = 400,
406 : .minSize = 300,
407 : .threshold = 240,
408 :
409 : .dkgInterval = 24 * 12, // one DKG every 12 hours
410 : .dkgPhaseBlocks = 4,
411 : .dkgMiningWindowStart = 20, // dkgPhaseBlocks * 5 = after finalization
412 : .dkgMiningWindowEnd = 28,
413 : .dkgBadVotesThreshold = 300,
414 :
415 : .signingActiveQuorumCount = 4, // two days worth of LLMQs
416 :
417 : .keepOldConnections = 5,
418 : .keepOldKeys = 8,
419 : .recoveryMembers = 100,
420 : },
421 :
422 : /**
423 : * llmq_400_85
424 : * This quorum is deployed on mainnet and requires
425 : * 300 - 400 participants _with_ a supermajority
426 : *
427 : * Used for deployment and min-proto-version signalling
428 : */
429 : LLMQParams{
430 : .type = LLMQType::LLMQ_400_85,
431 : .name = "llmq_400_85",
432 : .useRotation = false,
433 : .size = 400,
434 : .minSize = 350,
435 : .threshold = 340,
436 :
437 : .dkgInterval = 24 * 24, // one DKG every 24 hours
438 : .dkgPhaseBlocks = 4,
439 : .dkgMiningWindowStart = 20, // dkgPhaseBlocks * 5 = after finalization
440 : .dkgMiningWindowEnd = 48, // give it a larger mining window to make sure it is mined
441 : .dkgBadVotesThreshold = 300,
442 :
443 : .signingActiveQuorumCount = 4, // four days worth of LLMQs
444 :
445 : .keepOldConnections = 5,
446 : .keepOldKeys = 8,
447 : .recoveryMembers = 100,
448 : },
449 :
450 : /**
451 : * llmq_100_67
452 : * This quorum is deployed on mainnet and requires
453 : * 80 - 100 participants
454 : *
455 : * Used by Dash Platform
456 : */
457 : LLMQParams{
458 : .type = LLMQType::LLMQ_100_67,
459 : .name = "llmq_100_67",
460 : .useRotation = false,
461 : .size = 100,
462 : .minSize = 80,
463 : .threshold = 67,
464 :
465 : .dkgInterval = 24, // one DKG per hour
466 : .dkgPhaseBlocks = 2,
467 : .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
468 : .dkgMiningWindowEnd = 18,
469 : .dkgBadVotesThreshold = 80,
470 :
471 : .signingActiveQuorumCount = 24, // a full day worth of LLMQs
472 :
473 : .keepOldConnections = 25,
474 : .keepOldKeys = 24 * 30 * 2, // 2 months of quorums
475 : .recoveryMembers = 50,
476 : },
477 :
478 : /**
479 : * llmq_25_67
480 : * This quorum is deployed on Testnet and requires
481 : * 25 participants
482 : *
483 : * Used by Dash Platform
484 : */
485 : LLMQParams{
486 : .type = LLMQType::LLMQ_25_67,
487 : .name = "llmq_25_67",
488 : .useRotation = false,
489 : .size = 25,
490 : .minSize = 22,
491 : .threshold = 17,
492 :
493 : .dkgInterval = 24, // one DKG per hour
494 : .dkgPhaseBlocks = 2,
495 : .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization
496 : .dkgMiningWindowEnd = 18,
497 : .dkgBadVotesThreshold = 22,
498 :
499 : .signingActiveQuorumCount = 24, // a full day worth of LLMQs
500 :
501 : .keepOldConnections = 25,
502 : .keepOldKeys = 24 * 30 * 2, // 2 months of quorums
503 : .recoveryMembers = 12,
504 : },
505 :
506 : }; // available_llmqs
507 :
508 : } // namespace Consensus
509 :
510 : // This must be outside of all namespaces. We must also duplicate the forward declaration of is_serializable_enum to
511 : // avoid inclusion of serialize.h here.
512 : template<typename T> struct is_serializable_enum;
513 : template<> struct is_serializable_enum<Consensus::LLMQType> : std::true_type {};
514 :
515 : #endif // BITCOIN_LLMQ_PARAMS_H
|