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 29172 : 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 14586 : {
33 14586 : }
34 :
35 0 : bool CFinalCommitment::VerifySignatureAsync(const llmq::UtilParameters& util_params,
36 : CCheckQueueControl<utils::BlsCheck>* queue_control) const
37 : {
38 0 : auto members = utils::GetAllQuorumMembers(llmqType, util_params);
39 0 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
40 0 : 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 0 : const auto& llmq_params = llmq_params_opt.value();
46 :
47 0 : uint256 commitmentHash = BuildCommitmentHash(llmq_params.type, quorumHash, validMembers, quorumPublicKey,
48 0 : quorumVvecHash);
49 0 : if (LogAcceptDebug(BCLog::LLMQ)) {
50 0 : std::stringstream ss3;
51 0 : for (const auto& mn : members) {
52 0 : ss3 << mn->proTxHash.ToString().substr(0, 4) << " | ";
53 : }
54 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment::%s members[%s] quorumPublicKey[%s] commitmentHash[%s]\n", __func__,
55 : ss3.str(), quorumPublicKey.ToString(), commitmentHash.ToString());
56 0 : }
57 0 : if (llmq_params.is_single_member()) {
58 0 : LogPrintf("pubkey operator: %s\n", members[0]->pdmnState->pubKeyOperator.Get().ToString());
59 0 : 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 0 : } else {
64 0 : std::vector<CBLSPublicKey> memberPubKeys;
65 0 : for (const auto i : util::irange(members.size())) {
66 0 : if (!signers[i]) {
67 0 : continue;
68 : }
69 0 : memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get());
70 : }
71 0 : std::string members_id_string{
72 0 : strprintf("CFinalCommitment -- q[%s] invalid aggregated members signature", quorumHash.ToString())};
73 0 : if (queue_control) {
74 0 : std::vector<utils::BlsCheck> vChecks;
75 0 : vChecks.emplace_back(membersSig, memberPubKeys, commitmentHash, members_id_string);
76 0 : queue_control->Add(vChecks);
77 0 : } else {
78 0 : if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) {
79 0 : LogPrint(BCLog::LLMQ, "%s\n", members_id_string);
80 0 : return false;
81 : }
82 : }
83 0 : }
84 0 : std::string qsig_id_string{strprintf("CFinalCommitment -- q[%s] invalid quorum signature", quorumHash.ToString())};
85 0 : if (queue_control) {
86 0 : std::vector<utils::BlsCheck> vChecks;
87 0 : std::vector<CBLSPublicKey> public_keys;
88 0 : public_keys.push_back(quorumPublicKey);
89 0 : vChecks.emplace_back(quorumSig, public_keys, commitmentHash, qsig_id_string);
90 0 : queue_control->Add(vChecks);
91 0 : } else {
92 0 : if (!quorumSig.VerifyInsecure(quorumPublicKey, commitmentHash)) {
93 0 : LogPrint(BCLog::LLMQ, "%s\n", qsig_id_string);
94 0 : return false;
95 : }
96 : }
97 0 : return true;
98 0 : }
99 :
100 :
101 0 : bool CFinalCommitment::Verify(const llmq::UtilParameters& util_params, bool checkSigs) const
102 : {
103 0 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
104 0 : 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 0 : const auto& llmq_params = llmq_params_opt.value();
109 :
110 0 : const uint16_t expected_nversion{
111 0 : CFinalCommitment::GetVersion(IsQuorumRotationEnabled(llmq_params, util_params.m_base_index),
112 0 : DeploymentActiveAfter(util_params.m_base_index, util_params.m_chainman.GetConsensus(),
113 : Consensus::DEPLOYMENT_V19))};
114 0 : 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 0 : 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 0 : 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 0 : if (!VerifySizes(llmq_params)) {
130 0 : return false;
131 : }
132 :
133 0 : 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 0 : 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 0 : if (!quorumPublicKey.IsValid()) {
142 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorumPublicKey\n", quorumHash.ToString());
143 0 : return false;
144 : }
145 0 : 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 0 : if (!membersSig.IsValid()) {
150 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid membersSig\n", quorumHash.ToString());
151 0 : return false;
152 : }
153 0 : if (!quorumSig.IsValid()) {
154 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid vvecSig\n", quorumHash.ToString());
155 0 : return false;
156 : }
157 0 : auto members = utils::GetAllQuorumMembers(llmqType, util_params);
158 0 : if (LogAcceptDebug(BCLog::LLMQ)) {
159 0 : std::stringstream ss;
160 0 : std::stringstream ss2;
161 0 : for (const auto i : util::irange(llmq_params.size)) {
162 0 : ss << "v[" << i << "]=" << validMembers[i];
163 0 : ss2 << "s[" << i << "]=" << signers[i];
164 : }
165 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment::%s mns[%d] validMembers[%s] signers[%s]\n", __func__, members.size(), ss.str(), ss2.str());
166 0 : }
167 :
168 0 : for (const auto i : std::views::iota(members.size(), size_t(llmq_params.size))) {
169 0 : if (validMembers[i]) {
170 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid validMembers bitset. bit %d should not be set\n", quorumHash.ToString(), i);
171 0 : return false;
172 : }
173 0 : 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 0 : if (checkSigs) {
181 0 : if (!VerifySignatureAsync(util_params, /*queue_control=*/nullptr)) {
182 0 : return false;
183 : }
184 0 : }
185 :
186 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] VALID QUORUM\n", quorumHash.ToString());
187 :
188 0 : return true;
189 0 : }
190 :
191 58100 : bool CFinalCommitment::VerifyNull() const
192 : {
193 58100 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
194 58100 : 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 58100 : if (!IsNull() || !VerifySizes(llmq_params_opt.value())) {
200 0 : return false;
201 : }
202 :
203 58100 : return true;
204 58100 : }
205 :
206 58103 : bool CFinalCommitment::VerifySizes(const Consensus::LLMQParams& params) const
207 : {
208 58103 : 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 58101 : 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 58101 : return true;
218 58103 : }
219 :
220 29051 : bool CheckLLMQCommitment(const llmq::UtilParameters& util_params, const CTransaction& tx, TxValidationState& state)
221 : {
222 29051 : const auto opt_qcTx = GetTxPayload<CFinalCommitmentTxPayload>(tx);
223 29051 : 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 29051 : auto& qcTx = *opt_qcTx;
229 :
230 29051 : const auto& llmq_params_opt = Params().GetLLMQ(qcTx.commitment.llmqType);
231 29051 : if (!llmq_params_opt.has_value()) {
232 0 : 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 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-commitment-type");
235 : }
236 :
237 29051 : 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 29051 : std::stringstream ss;
241 29051 : const auto log_size = std::min<size_t>(llmq_params_opt->size, qcTx.commitment.validMembers.size());
242 129785 : for (const auto i : util::irange(log_size)) {
243 100734 : ss << "v[" << i << "]=" << qcTx.commitment.validMembers[i];
244 : }
245 29051 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- %s llmqType[%d] validMembers[%s] signers[]\n", __func__,
246 : int(qcTx.commitment.llmqType), ss.str());
247 29051 : }
248 :
249 29051 : 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 29051 : 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 29050 : const CBlockIndex* pQuorumBaseBlockIndex =
262 58100 : WITH_LOCK(::cs_main, return util_params.m_chainman.m_blockman.LookupBlockIndex(qcTx.commitment.quorumHash));
263 29050 : if (pQuorumBaseBlockIndex == nullptr) {
264 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-quorum-hash");
265 : }
266 :
267 29050 : 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 29050 : if (qcTx.commitment.IsNull()) {
273 29050 : 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 29050 : return true;
279 : }
280 :
281 0 : if (!qcTx.commitment.Verify(util_params.replace_index(pQuorumBaseBlockIndex), false)) {
282 0 : 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 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid");
285 : }
286 :
287 0 : LogPrint(BCLog::LLMQ, "CFinalCommitment -- h[%d] CheckLLMQCommitment VALID\n", util_params.m_base_index->nHeight);
288 :
289 0 : return true;
290 29051 : }
291 :
292 28 : uint256 BuildCommitmentHash(Consensus::LLMQType llmqType, const uint256& blockHash,
293 : const std::vector<bool>& validMembers, const CBLSPublicKey& pubKey,
294 : const uint256& vvecHash)
295 : {
296 28 : CHashWriter hw(SER_GETHASH, 0);
297 28 : hw << llmqType;
298 28 : hw << blockHash;
299 28 : hw << DYNBITSET(validMembers);
300 28 : hw << pubKey;
301 28 : hw << vvecHash;
302 28 : return hw.GetHash();
303 : }
304 :
305 : } // namespace llmq
|