Line data Source code
1 : // Copyright (c) 2018-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <llmq/commitment.h>
6 :
7 : #include <evo/deterministicmns.h>
8 : #include <evo/specialtx.h>
9 : #include <llmq/options.h>
10 : #include <llmq/utils.h>
11 : #include <util/helpers.h>
12 : #include <util/std23.h>
13 :
14 : #include <chainparams.h>
15 : #include <checkqueue.h>
16 : #include <consensus/validation.h>
17 : #include <deploymentstatus.h>
18 : #include <logging.h>
19 : #include <validation.h>
20 :
21 : #include <algorithm>
22 : #include <ranges>
23 :
24 : namespace llmq
25 : {
26 :
27 119896 : CFinalCommitment::CFinalCommitment(const Consensus::LLMQParams& params, const uint256& _quorumHash) :
28 : llmqType(params.type),
29 : quorumHash(_quorumHash),
30 : signers(params.size),
31 : validMembers(params.size)
32 59948 : {
33 59948 : }
34 :
35 9310 : bool CFinalCommitment::VerifySignatureAsync(const llmq::UtilParameters& util_params,
36 : CCheckQueueControl<utils::BlsCheck>* queue_control) const
37 : {
38 9310 : auto members = utils::GetAllQuorumMembers(llmqType, util_params);
39 9310 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
40 9310 : if (!llmq_params_opt.has_value()) {
41 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid llmqType=%d\n", quorumHash.ToString(),
42 : std23::to_underlying(llmqType));
43 0 : return false;
44 : }
45 9310 : const auto& llmq_params = llmq_params_opt.value();
46 :
47 18620 : uint256 commitmentHash = BuildCommitmentHash(llmq_params.type, quorumHash, validMembers, quorumPublicKey,
48 9310 : quorumVvecHash);
49 9310 : if (LogAcceptDebug(BCLog::LLMQ)) {
50 9310 : std::stringstream ss3;
51 43431 : for (const auto& mn : members) {
52 34121 : ss3 << mn->proTxHash.ToString().substr(0, 4) << " | ";
53 : }
54 9310 : LogPrint(BCLog::LLMQ, "CFinalCommitment::%s members[%s] quorumPublicKey[%s] commitmentHash[%s]\n", __func__,
55 : ss3.str(), quorumPublicKey.ToString(), commitmentHash.ToString());
56 9310 : }
57 9310 : if (llmq_params.is_single_member()) {
58 215 : LogPrintf("pubkey operator: %s\n", members[0]->pdmnState->pubKeyOperator.Get().ToString());
59 215 : if (!membersSig.VerifyInsecure(members[0]->pdmnState->pubKeyOperator.Get(), commitmentHash)) {
60 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid member signature\n", quorumHash.ToString());
61 0 : return false;
62 : }
63 215 : } else {
64 9095 : std::vector<CBLSPublicKey> memberPubKeys;
65 43001 : for (const auto i : util::irange(members.size())) {
66 33906 : if (!signers[i]) {
67 449 : continue;
68 : }
69 33457 : memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get());
70 : }
71 9095 : std::string members_id_string{
72 9095 : strprintf("CFinalCommitment -- q[%s] invalid aggregated members signature", quorumHash.ToString())};
73 9095 : if (queue_control) {
74 4805 : std::vector<utils::BlsCheck> vChecks;
75 4805 : vChecks.emplace_back(membersSig, memberPubKeys, commitmentHash, members_id_string);
76 4805 : queue_control->Add(vChecks);
77 4805 : } else {
78 4290 : if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) {
79 0 : LogPrint(BCLog::LLMQ, "%s\n", members_id_string);
80 0 : return false;
81 : }
82 : }
83 9095 : }
84 9310 : std::string qsig_id_string{strprintf("CFinalCommitment -- q[%s] invalid quorum signature", quorumHash.ToString())};
85 9310 : if (queue_control) {
86 4921 : std::vector<utils::BlsCheck> vChecks;
87 4921 : std::vector<CBLSPublicKey> public_keys;
88 4921 : public_keys.push_back(quorumPublicKey);
89 4921 : vChecks.emplace_back(quorumSig, public_keys, commitmentHash, qsig_id_string);
90 4921 : queue_control->Add(vChecks);
91 4921 : } else {
92 4389 : if (!quorumSig.VerifyInsecure(quorumPublicKey, commitmentHash)) {
93 0 : LogPrint(BCLog::LLMQ, "%s\n", qsig_id_string);
94 0 : return false;
95 : }
96 : }
97 9310 : return true;
98 9310 : }
99 :
100 :
101 14231 : bool CFinalCommitment::Verify(const llmq::UtilParameters& util_params, bool checkSigs) const
102 : {
103 14231 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
104 14231 : if (!llmq_params_opt.has_value()) {
105 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid llmqType=%d\n", quorumHash.ToString(), std23::to_underlying(llmqType));
106 0 : return false;
107 : }
108 14231 : const auto& llmq_params = llmq_params_opt.value();
109 :
110 14231 : const uint16_t expected_nversion{
111 28462 : CFinalCommitment::GetVersion(IsQuorumRotationEnabled(llmq_params, util_params.m_base_index),
112 14231 : DeploymentActiveAfter(util_params.m_base_index, util_params.m_chainman.GetConsensus(),
113 : Consensus::DEPLOYMENT_V19))};
114 14231 : if (nVersion == 0 || nVersion != expected_nversion) {
115 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid nVersion=%d expected=%d\n", quorumHash.ToString(), nVersion, expected_nversion);
116 0 : return false;
117 : }
118 :
119 14231 : if (util_params.m_base_index->GetBlockHash() != quorumHash) {
120 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumHash\n", quorumHash.ToString());
121 0 : return false;
122 : }
123 :
124 14231 : if ((util_params.m_base_index->nHeight % llmq_params.dkgInterval) != quorumIndex) {
125 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumIndex=%d\n", quorumHash.ToString(), quorumIndex);
126 0 : return false;
127 : }
128 :
129 14231 : if (!VerifySizes(llmq_params)) {
130 0 : return false;
131 : }
132 :
133 14231 : if (CountValidMembers() < llmq_params.minSize) {
134 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid validMembers count. validMembersCount=%d\n", quorumHash.ToString(), CountValidMembers());
135 0 : return false;
136 : }
137 14231 : if (CountSigners() < llmq_params.minSize) {
138 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid signers count. signersCount=%d\n", quorumHash.ToString(), CountSigners());
139 0 : return false;
140 : }
141 14231 : if (!quorumPublicKey.IsValid()) {
142 2 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumPublicKey\n", quorumHash.ToString());
143 2 : return false;
144 : }
145 14229 : if (!llmq_params.is_single_member() && quorumVvecHash.IsNull()) {
146 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumVvecHash\n", quorumHash.ToString());
147 0 : return false;
148 : }
149 14229 : if (!membersSig.IsValid()) {
150 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid membersSig\n", quorumHash.ToString());
151 0 : return false;
152 : }
153 14229 : if (!quorumSig.IsValid()) {
154 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid vvecSig\n", quorumHash.ToString());
155 0 : return false;
156 : }
157 14229 : auto members = utils::GetAllQuorumMembers(llmqType, util_params);
158 14229 : if (LogAcceptDebug(BCLog::LLMQ)) {
159 14229 : std::stringstream ss;
160 14229 : std::stringstream ss2;
161 67833 : for (const auto i : util::irange(llmq_params.size)) {
162 53604 : ss << "v[" << i << "]=" << validMembers[i];
163 53604 : ss2 << "s[" << i << "]=" << signers[i];
164 : }
165 14229 : LogPrint(BCLog::LLMQ, "CFinalCommitment::%s mns[%d] validMembers[%s] signers[%s]\n", __func__, members.size(), ss.str(), ss2.str());
166 14229 : }
167 :
168 15889 : for (const auto i : std::views::iota(members.size(), size_t(llmq_params.size))) {
169 1662 : if (validMembers[i]) {
170 2 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid validMembers bitset. bit %d should not be set\n", quorumHash.ToString(), i);
171 2 : return false;
172 : }
173 1660 : if (signers[i]) {
174 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid signers bitset. bit %d should not be set\n", quorumHash.ToString(), i);
175 0 : return false;
176 : }
177 : }
178 :
179 : // sigs are only checked when the block is processed
180 14227 : if (checkSigs) {
181 4389 : if (!VerifySignatureAsync(util_params, /*queue_control=*/nullptr)) {
182 0 : return false;
183 : }
184 4389 : }
185 :
186 14227 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] VALID QUORUM\n", quorumHash.ToString());
187 :
188 14227 : return true;
189 14231 : }
190 :
191 456940 : bool CFinalCommitment::VerifyNull() const
192 : {
193 456940 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
194 456940 : if (!llmq_params_opt.has_value()) {
195 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s]invalid llmqType=%d\n", quorumHash.ToString(), std23::to_underlying(llmqType));
196 0 : return false;
197 : }
198 :
199 456940 : if (!IsNull() || !VerifySizes(llmq_params_opt.value())) {
200 0 : return false;
201 : }
202 :
203 456940 : return true;
204 456940 : }
205 :
206 471174 : bool CFinalCommitment::VerifySizes(const Consensus::LLMQParams& params) const
207 : {
208 471174 : if (signers.size() != size_t(params.size)) {
209 2 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid signers.size=%d\n", quorumHash.ToString(), signers.size());
210 2 : return false;
211 : }
212 471172 : if (validMembers.size() != size_t(params.size)) {
213 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid validMembers.size=%d\n", quorumHash.ToString(),
214 : validMembers.size());
215 0 : return false;
216 : }
217 471172 : return true;
218 471174 : }
219 :
220 233400 : bool CheckLLMQCommitment(const llmq::UtilParameters& util_params, const CTransaction& tx, TxValidationState& state)
221 : {
222 233400 : const auto opt_qcTx = GetTxPayload<CFinalCommitmentTxPayload>(tx);
223 233400 : if (!opt_qcTx) {
224 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] GetTxPayload LLMQCommitment failed\n",
225 : util_params.m_base_index->nHeight);
226 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-payload");
227 : }
228 233400 : auto& qcTx = *opt_qcTx;
229 :
230 233400 : const auto& llmq_params_opt = Params().GetLLMQ(qcTx.commitment.llmqType);
231 233400 : if (!llmq_params_opt.has_value()) {
232 2 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] GetLLMQ failed for llmqType[%d]\n",
233 : util_params.m_base_index->nHeight, std23::to_underlying(qcTx.commitment.llmqType));
234 2 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-commitment-type");
235 : }
236 :
237 233398 : if (LogAcceptDebug(BCLog::LLMQ)) {
238 : // Clamp to validMembers.size() because the wire-format DYNBITSET may be smaller than
239 : // llmq_params.size for malformed payloads; VerifySizes() below catches the mismatch.
240 233398 : std::stringstream ss;
241 233398 : const auto log_size = std::min<size_t>(llmq_params_opt->size, qcTx.commitment.validMembers.size());
242 1080487 : for (const auto i : util::irange(log_size)) {
243 847089 : ss << "v[" << i << "]=" << qcTx.commitment.validMembers[i];
244 : }
245 233398 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- %s llmqType[%d] validMembers[%s] signers[]\n", __func__,
246 : int(qcTx.commitment.llmqType), ss.str());
247 233398 : }
248 :
249 233398 : if (qcTx.nVersion == 0 || qcTx.nVersion > CFinalCommitmentTxPayload::CURRENT_VERSION) {
250 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.nVersion[%d]\n",
251 : util_params.m_base_index->nHeight, qcTx.nVersion);
252 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-version");
253 : }
254 :
255 233398 : if (qcTx.nHeight != uint32_t(util_params.m_base_index->nHeight + 1)) {
256 1 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.nHeight[%d]\n", util_params.m_base_index->nHeight,
257 : qcTx.nHeight);
258 1 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-height");
259 : }
260 :
261 233397 : const CBlockIndex* pQuorumBaseBlockIndex =
262 466794 : WITH_LOCK(::cs_main, return util_params.m_chainman.m_blockman.LookupBlockIndex(qcTx.commitment.quorumHash));
263 233397 : if (pQuorumBaseBlockIndex == nullptr) {
264 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-quorum-hash");
265 : }
266 :
267 233397 : if (pQuorumBaseBlockIndex != util_params.m_base_index->GetAncestor(pQuorumBaseBlockIndex->nHeight)) {
268 : // not part of active chain
269 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-quorum-hash");
270 : }
271 :
272 233397 : if (qcTx.commitment.IsNull()) {
273 228472 : if (!qcTx.commitment.VerifyNull()) {
274 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.commitment[%s] VerifyNull failed\n",
275 : util_params.m_base_index->nHeight, qcTx.commitment.quorumHash.ToString());
276 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid-null");
277 : }
278 228472 : return true;
279 : }
280 :
281 4925 : if (!qcTx.commitment.Verify(util_params.replace_index(pQuorumBaseBlockIndex), false)) {
282 4 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] invalid qcTx.commitment[%s] Verify failed\n",
283 : util_params.m_base_index->nHeight, qcTx.commitment.quorumHash.ToString());
284 4 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid");
285 : }
286 :
287 4921 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] CheckLLMQCommitment VALID\n", util_params.m_base_index->nHeight);
288 :
289 4921 : return true;
290 233400 : }
291 :
292 31189 : uint256 BuildCommitmentHash(Consensus::LLMQType llmqType, const uint256& blockHash,
293 : const std::vector<bool>& validMembers, const CBLSPublicKey& pubKey,
294 : const uint256& vvecHash)
295 : {
296 31189 : CHashWriter hw(SER_GETHASH, 0);
297 31189 : hw << llmqType;
298 31189 : hw << blockHash;
299 31189 : hw << DYNBITSET(validMembers);
300 31189 : hw << pubKey;
301 31189 : hw << vvecHash;
302 31189 : return hw.GetHash();
303 : }
304 :
305 : } // namespace llmq
|