Line data Source code
1 : // Copyright (c) 2017 The Dash Core developers
2 : // Copyright (c) 2020-2022 The PIVX Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "evo/specialtx_validation.h"
7 :
8 : #include "chain.h"
9 : #include "coins.h"
10 : #include "chainparams.h"
11 : #include "clientversion.h"
12 : #include "consensus/validation.h"
13 : #include "evo/deterministicmns.h"
14 : #include "evo/providertx.h"
15 : #include "llmq/quorums_blockprocessor.h"
16 : #include "messagesigner.h"
17 : #include "primitives/transaction.h"
18 : #include "primitives/block.h"
19 : #include "script/standard.h"
20 : #include "spork.h"
21 :
22 : /* -- Helper static functions -- */
23 :
24 1564 : static bool CheckService(const CService& addr, CValidationState& state)
25 : {
26 1564 : if (!addr.IsValid()) {
27 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-ipaddr");
28 : }
29 1564 : if (!Params().IsRegTestNet() && !addr.IsRoutable()) {
30 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-ipaddr");
31 : }
32 :
33 : // IP port must be the default one on main-net, which cannot be used on other nets.
34 1637 : static int mainnetDefaultPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPort();
35 1564 : if (Params().NetworkIDString() == CBaseChainParams::MAIN) {
36 2 : if (addr.GetPort() != mainnetDefaultPort) {
37 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-ipaddr-port");
38 : }
39 1562 : } else if (addr.GetPort() == mainnetDefaultPort) {
40 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-ipaddr-port");
41 : }
42 :
43 : // !TODO: add support for IPv6 and Tor
44 1564 : if (!addr.IsIPv4()) {
45 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-ipaddr");
46 : }
47 :
48 : return true;
49 : }
50 :
51 : template <typename Payload>
52 85 : static bool CheckHashSig(const Payload& pl, const CKeyID& keyID, CValidationState& state)
53 : {
54 85 : std::string strError;
55 85 : if (!CHashSigner::VerifyHash(::SerializeHash(pl), keyID, pl.vchSig, strError)) {
56 4 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError);
57 : }
58 : return true;
59 : }
60 :
61 : template <typename Payload>
62 90 : static bool CheckHashSig(const Payload& pl, const CBLSPublicKey& pubKey, CValidationState& state)
63 : {
64 90 : if (!pl.sig.VerifyInsecure(pubKey, ::SerializeHash(pl))) {
65 6 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false);
66 : }
67 : return true;
68 : }
69 :
70 : template <typename Payload>
71 457 : static bool CheckStringSig(const Payload& pl, const CKeyID& keyID, CValidationState& state)
72 : {
73 457 : std::string strError;
74 914 : if (!CMessageSigner::VerifyMessage(keyID, pl.vchSig, pl.MakeSignString(), strError)) {
75 0 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError);
76 : }
77 : return true;
78 : }
79 :
80 : template <typename Payload>
81 1767 : static bool CheckInputsHash(const CTransaction& tx, const Payload& pl, CValidationState& state)
82 : {
83 1767 : if (CalcTxInputsHash(tx) != pl.inputsHash) {
84 3 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-inputs-hash");
85 : }
86 :
87 : return true;
88 : }
89 :
90 1195 : static bool CheckCollateralOut(const CTxOut& out, const ProRegPL& pl, CValidationState& state, CTxDestination& collateralDestRet)
91 : {
92 1195 : if (!ExtractDestination(out.scriptPubKey, collateralDestRet)) {
93 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-dest");
94 : }
95 : // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
96 : // this check applies to internal and external collateral, but internal collaterals are not necessarely a P2PKH
97 3584 : if (collateralDestRet == CTxDestination(pl.keyIDOwner) ||
98 2389 : collateralDestRet == CTxDestination(pl.keyIDVoting)) {
99 3 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse");
100 : }
101 : // check collateral amount
102 1194 : if (out.nValue != Params().GetConsensus().nMNCollateralAmt) {
103 6 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral-amount");
104 : }
105 : return true;
106 : }
107 :
108 : // Provider Register Payload
109 1472 : static bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache* view, CValidationState& state)
110 : {
111 :
112 2944 : ProRegPL pl;
113 1472 : if (!GetValidatedTxPayload(tx, pl, state)) {
114 : // pass the state returned by the function above
115 : return false;
116 : }
117 :
118 : // It's allowed to set addr to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be issues later
119 : // If any of both is set, it must be valid however
120 2942 : if (pl.addr != CService() && !CheckService(pl.addr, state)) {
121 : // pass the state returned by the function above
122 : return false;
123 : }
124 :
125 25786 : if (pl.collateralOutpoint.hash.IsNull()) {
126 : // collateral included in the proReg tx
127 736 : if (pl.collateralOutpoint.n >= tx.vout.size()) {
128 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-index");
129 : }
130 1471 : CTxDestination collateralTxDest;
131 736 : if (!CheckCollateralOut(tx.vout[pl.collateralOutpoint.n], pl, state, collateralTxDest)) {
132 : // pass the state returned by the function above
133 1 : return false;
134 : }
135 : // collateral is part of this ProRegTx, so we know the collateral is owned by the issuer
136 735 : if (!pl.vchSig.empty()) {
137 0 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig");
138 : }
139 735 : } else if (pindexPrev != nullptr) {
140 463 : assert(view != nullptr);
141 :
142 : // Referenced external collateral.
143 : // This is checked only when pindexPrev is not null (thus during ConnectBlock-->CheckSpecialTx),
144 : // because this is a contextual check: we need the updated utxo set, to verify that
145 : // the coin exists and it is unspent.
146 :
147 : // Spending the declared collateral in the same tx halts block production.
148 : // AcceptToMemoryPool calls this before applying inputs to the view, so GetUTXOCoin
149 : // below still finds the coin and the tx is accepted. ConnectBlock runs UpdateCoins
150 : // (validation.cpp:1602) before ProcessSpecialTxsInBlock (:1671), so by then the coin
151 : // is spent, CheckSpecialTx fails, and BlockAssembler throws instead of dropping the
152 : // tx (blockassembler.cpp:253). Reject at mempool time. Checked ahead of GetUTXOCoin
153 : // so the reject reason names the real cause at both call sites.
154 925 : for (const CTxIn& txin : tx.vin) {
155 553 : if (txin.prevout == pl.collateralOutpoint) {
156 6 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-used-as-input");
157 : }
158 : }
159 :
160 918 : Coin coin;
161 461 : if (!view->GetUTXOCoin(pl.collateralOutpoint, coin)) {
162 6 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral");
163 : }
164 916 : CTxDestination collateralTxDest;
165 459 : if (!CheckCollateralOut(coin.out, pl, state, collateralTxDest)) {
166 : // pass the state returned by the function above
167 4 : return false;
168 : }
169 : // Extract key from collateral. This only works for P2PK and P2PKH collaterals and will fail for P2SH.
170 : // Issuer of this ProRegTx must prove ownership with this key by signing the ProRegTx
171 457 : const CKeyID* keyForPayloadSig = boost::get<CKeyID>(&collateralTxDest);
172 457 : if (!keyForPayloadSig) {
173 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-pkh");
174 : }
175 : // collateral is not part of this ProRegTx, so we must verify ownership of the collateral
176 457 : if (!CheckStringSig(pl, *keyForPayloadSig, state)) {
177 : // pass the state returned by the function above
178 : return false;
179 : }
180 : }
181 :
182 1464 : if (!CheckInputsHash(tx, pl, state)) {
183 : return false;
184 : }
185 :
186 1463 : if (pindexPrev) {
187 1799 : auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
188 : // only allow reusing of addresses when it's for the same collateral (which replaces the old MN)
189 906 : if (mnList.HasUniqueProperty(pl.addr) && mnList.GetUniquePropertyMN(pl.addr)->collateralOutpoint != pl.collateralOutpoint) {
190 9 : return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-IP-address");
191 : }
192 : // never allow duplicate keys, even if this ProTx would replace an existing MN
193 900 : if (mnList.HasUniqueProperty(pl.keyIDOwner)) {
194 6 : return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-owner-key");
195 : }
196 898 : if (mnList.HasUniqueProperty(pl.pubKeyOperator)) {
197 6 : return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-operator-key");
198 : }
199 : }
200 :
201 : return true;
202 : }
203 :
204 : // Provider Update Service Payload
205 93 : static bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
206 : {
207 :
208 186 : ProUpServPL pl;
209 93 : if (!GetValidatedTxPayload(tx, pl, state)) {
210 : // pass the state returned by the function above
211 : return false;
212 : }
213 :
214 93 : if (!CheckService(pl.addr, state)) {
215 : // pass the state returned by the function above
216 : return false;
217 : }
218 :
219 93 : if (!CheckInputsHash(tx, pl, state)) {
220 : // pass the state returned by the function above
221 : return false;
222 : }
223 :
224 93 : if (pindexPrev) {
225 55 : auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
226 105 : auto mn = mnList.GetMN(pl.proTxHash);
227 55 : if (!mn) {
228 2 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
229 : }
230 :
231 : // don't allow updating to addresses already used by other MNs
232 69 : if (mnList.HasUniqueProperty(pl.addr) && mnList.GetUniquePropertyMN(pl.addr)->proTxHash != pl.proTxHash) {
233 6 : return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
234 : }
235 :
236 52 : if (!pl.scriptOperatorPayout.empty()) {
237 13 : if (mn->nOperatorReward == 0) {
238 : // don't allow to set operator reward payee in case no operatorReward was set
239 3 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee");
240 : }
241 : // we may support other kinds of scripts later, but restrict it for now
242 12 : if (!pl.scriptOperatorPayout.IsPayToPublicKeyHash()) {
243 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee");
244 : }
245 : }
246 :
247 : // we can only check the signature if pindexPrev != nullptr and the MN is known
248 51 : if (!CheckHashSig(pl, mn->pdmnState->pubKeyOperator.Get(), state)) {
249 : // pass the state returned by the function above
250 : return false;
251 : }
252 : }
253 :
254 : return true;
255 : }
256 :
257 : // Provider Update Registrar Payload
258 146 : static bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache* view, CValidationState& state)
259 : {
260 :
261 146 : ProUpRegPL pl;
262 146 : if (!GetValidatedTxPayload(tx, pl, state)) {
263 : // pass the state returned by the function above
264 : return false;
265 : }
266 :
267 292 : CTxDestination payoutDest;
268 146 : if (!ExtractDestination(pl.scriptPayout, payoutDest)) {
269 : // should not happen as we checked script types before
270 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest");
271 : }
272 :
273 : // don't allow reuse of payee key for other keys
274 146 : if (payoutDest == CTxDestination(pl.keyIDVoting)) {
275 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse");
276 : }
277 :
278 146 : if (!CheckInputsHash(tx, pl, state)) {
279 : return false;
280 : }
281 :
282 146 : if (pindexPrev) {
283 88 : assert(view != nullptr);
284 :
285 : // ProUpReg txes are disabled when the legacy system is still active
286 : // !TODO: remove after complete transition to DMN
287 88 : if (!deterministicMNManager->LegacyMNObsolete(pindexPrev->nHeight + 1)) {
288 0 : return state.DoS(10, false, REJECT_INVALID, "spork-21-inactive");
289 : }
290 :
291 88 : auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
292 171 : auto dmn = mnList.GetMN(pl.proTxHash);
293 88 : if (!dmn) {
294 2 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
295 : }
296 :
297 : // don't allow reuse of payee key for owner key
298 87 : if (payoutDest == CTxDestination(dmn->pdmnState->keyIDOwner)) {
299 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse");
300 : }
301 :
302 170 : Coin coin;
303 87 : if (!view->GetUTXOCoin(dmn->collateralOutpoint, coin)) {
304 : // this should never happen (there would be no dmn otherwise)
305 0 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral");
306 : }
307 :
308 : // don't allow reuse of collateral key for other keys (don't allow people to put the payee key onto an online server)
309 170 : CTxDestination collateralTxDest;
310 87 : if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) {
311 0 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral-dest");
312 : }
313 261 : if (collateralTxDest == CTxDestination(dmn->pdmnState->keyIDOwner) ||
314 174 : collateralTxDest == CTxDestination(pl.keyIDVoting)) {
315 0 : return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse");
316 : }
317 :
318 87 : if (mnList.HasUniqueProperty(pl.pubKeyOperator)) {
319 72 : auto otherDmn = mnList.GetUniquePropertyMN(pl.pubKeyOperator);
320 37 : if (pl.proTxHash != otherDmn->proTxHash) {
321 6 : return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key");
322 : }
323 : }
324 :
325 85 : if (!CheckHashSig(pl, dmn->pdmnState->keyIDOwner, state)) {
326 : // pass the state returned by the function above
327 : return false;
328 : }
329 :
330 : }
331 :
332 : return true;
333 : }
334 :
335 : // Provider Update Revoke Payload
336 64 : static bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
337 : {
338 :
339 64 : ProUpRevPL pl;
340 64 : if (!GetValidatedTxPayload(tx, pl, state)) {
341 : // pass the state returned by the function above
342 : return false;
343 : }
344 :
345 64 : if (!CheckInputsHash(tx, pl, state)) {
346 : // pass the state returned by the function above
347 : return false;
348 : }
349 :
350 64 : if (pindexPrev) {
351 39 : auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
352 76 : auto dmn = mnList.GetMN(pl.proTxHash);
353 39 : if (!dmn)
354 0 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
355 :
356 39 : if (!CheckHashSig(pl, dmn->pdmnState->pubKeyOperator.Get(), state)) {
357 : // pass the state returned by the function above
358 : return false;
359 : }
360 : }
361 :
362 : return true;
363 : }
364 :
365 : // LLMQ final commitment Payload
366 5584 : bool VerifyLLMQCommitment(const llmq::CFinalCommitment& qfc, const CBlockIndex* pindexPrev, CValidationState& state)
367 : {
368 5584 : AssertLockHeld(cs_main);
369 :
370 : // Check DKG maintenance mode
371 5584 : if (sporkManager.IsSporkActive(SPORK_22_LLMQ_DKG_MAINTENANCE) && !IsInitialBlockDownload()) {
372 : // only null commitments are accepted
373 182 : if (!qfc.IsNull()) {
374 9 : return state.DoS(50, false, REJECT_INVALID, "bad-qc-not-null-spork22");
375 : }
376 : }
377 :
378 : // Check version
379 5581 : if (qfc.nVersion == 0 || qfc.nVersion > llmq::CFinalCommitment::CURRENT_VERSION) {
380 0 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-version");
381 : }
382 :
383 : // Check type
384 11165 : Optional<Consensus::LLMQParams> params = Params().GetConsensus().GetLLMQParams(qfc.llmqType);
385 5581 : if (params == nullopt) {
386 0 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-type");
387 : }
388 :
389 : // Check sizes
390 5581 : if (!qfc.VerifySizes(*params)) {
391 0 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-sizes");
392 : }
393 :
394 5581 : if (pindexPrev) {
395 : // Get quorum index
396 2668 : CBlockIndex* pindexQuorum = LookupBlockIndex(qfc.quorumHash);
397 2668 : if (!pindexQuorum) {
398 3 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-hash-not-found");
399 : }
400 :
401 : // Check height
402 2667 : if (pindexQuorum->nHeight % params->dkgInterval != 0) {
403 : // not first block of DKG interval
404 3 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-height");
405 : }
406 :
407 : // Check height limit
408 2666 : if (pindexPrev->nHeight - pindexQuorum->nHeight > params->cacheDkgInterval) {
409 6 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-height-old");
410 : }
411 :
412 2664 : if (pindexQuorum != pindexPrev->GetAncestor(pindexQuorum->nHeight)) {
413 : // not part of active chain
414 3 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-hash-not-active-chain");
415 : }
416 :
417 : // Get members and check signatures (for not-null commitments)
418 2663 : if (!qfc.IsNull()) {
419 586 : std::vector<CBLSPublicKey> allkeys;
420 1172 : for (const auto& m : deterministicMNManager->GetAllQuorumMembers((Consensus::LLMQType)qfc.llmqType, pindexQuorum)) {
421 879 : allkeys.emplace_back(m->pdmnState->pubKeyOperator.Get());
422 : }
423 293 : if (!qfc.Verify(allkeys, *params)) {
424 0 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid");
425 : }
426 : }
427 : }
428 :
429 : return true;
430 : }
431 :
432 5468 : static bool CheckLLMQCommitmentTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
433 : {
434 5468 : AssertLockHeld(cs_main);
435 :
436 10936 : llmq::LLMQCommPL pl;
437 5468 : if (!GetTxPayload(tx, pl)) {
438 0 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-payload");
439 : }
440 :
441 5468 : if (pl.nVersion == 0 || pl.nVersion > llmq::LLMQCommPL::CURRENT_VERSION) {
442 0 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-version");
443 : }
444 :
445 5468 : if (pindexPrev && pl.nHeight != (uint32_t)pindexPrev->nHeight + 1) {
446 2 : return state.DoS(100, false, REJECT_INVALID, "bad-qc-height");
447 : }
448 :
449 5467 : return VerifyLLMQCommitment(pl.commitment, pindexPrev, state);
450 : }
451 :
452 : // Basic non-contextual checks for all tx types
453 1558162 : static bool CheckSpecialTxBasic(const CTransaction& tx, CValidationState& state)
454 : {
455 1558162 : bool hasExtraPayload = tx.hasExtraPayload();
456 :
457 1558162 : if (tx.IsNormalType()) {
458 : // Type-0 txes don't have extra payload
459 1550915 : if (hasExtraPayload) {
460 3 : return state.DoS(100, error("%s: Type 0 doesn't support extra payload", __func__),
461 : REJECT_INVALID, "bad-txns-type-payload");
462 : }
463 : // Normal transaction. Nothing to check
464 : return true;
465 : }
466 :
467 : // Special txes need at least version 2
468 7246 : if (!tx.isSaplingVersion()) {
469 3 : return state.DoS(100, error("%s: Type %d not supported with version %d", __func__, tx.nType, tx.nVersion),
470 : REJECT_INVALID, "bad-txns-type-version");
471 : }
472 :
473 : // Cannot be coinbase/coinstake tx
474 7245 : if (tx.IsCoinBase() || tx.IsCoinStake()) {
475 0 : return state.DoS(10, error("%s: Special tx is coinbase or coinstake", __func__),
476 : REJECT_INVALID, "bad-txns-special-coinbase");
477 : }
478 :
479 : // Special txes must have a non-empty payload
480 7245 : if (!hasExtraPayload) {
481 3 : return state.DoS(100, error("%s: Special tx (type=%d) without extra payload", __func__, tx.nType),
482 : REJECT_INVALID, "bad-txns-payload-empty");
483 : }
484 :
485 : // Size limits
486 7244 : if (tx.extraPayload->size() > MAX_SPECIALTX_EXTRAPAYLOAD) {
487 3 : return state.DoS(100, error("%s: Special tx payload oversize (%d)", __func__, tx.extraPayload->size()),
488 : REJECT_INVALID, "bad-txns-payload-oversize");
489 : }
490 :
491 : return true;
492 : }
493 :
494 : // contextual and non-contextual per-type checks
495 : // - pindexPrev=null: CheckBlock-->CheckSpecialTxNoContext
496 : // - pindexPrev=chainActive.Tip: AcceptToMemoryPoolWorker-->CheckSpecialTx
497 : // - pindexPrev=pindex->pprev: ConnectBlock-->ProcessSpecialTxsInBlock-->CheckSpecialTx
498 1558162 : bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache* view, CValidationState& state)
499 : {
500 1558162 : AssertLockHeld(cs_main);
501 :
502 1558162 : if (!CheckSpecialTxBasic(tx, state)) {
503 : // pass the state returned by the function above
504 : return false;
505 : }
506 1558158 : if (pindexPrev) {
507 : // reject special transactions before enforcement
508 734386 : if (!tx.IsNormalType() && !Params().GetConsensus().NetworkUpgradeActive(pindexPrev->nHeight + 1, Consensus::UPGRADE_V6_0)) {
509 0 : return state.DoS(100, error("%s: Special tx when v6 upgrade not enforced yet", __func__),
510 : REJECT_INVALID, "bad-txns-v6-not-active");
511 : }
512 : }
513 : // per-type checks
514 1558158 : switch (tx.nType) {
515 : case CTransaction::TxType::NORMAL: {
516 : // nothing to check
517 : return true;
518 : }
519 1472 : case CTransaction::TxType::PROREG: {
520 : // provider-register
521 1472 : return CheckProRegTx(tx, pindexPrev, view, state);
522 : }
523 93 : case CTransaction::TxType::PROUPSERV: {
524 : // provider-update-service
525 93 : return CheckProUpServTx(tx, pindexPrev, state);
526 : }
527 146 : case CTransaction::TxType::PROUPREG: {
528 : // provider-update-registrar
529 146 : return CheckProUpRegTx(tx, pindexPrev, view, state);
530 : }
531 64 : case CTransaction::TxType::PROUPREV: {
532 : // provider-update-revoke
533 64 : return CheckProUpRevTx(tx, pindexPrev, state);
534 : }
535 5468 : case CTransaction::TxType::LLMQCOMM: {
536 : // quorum commitment
537 5468 : return CheckLLMQCommitmentTx(tx, pindexPrev, state);
538 : }
539 : }
540 :
541 0 : return state.DoS(10, error("%s: special tx %s with invalid type %d", __func__, tx.GetHash().ToString(), tx.nType),
542 : REJECT_INVALID, "bad-tx-type");
543 : }
544 :
545 823776 : bool CheckSpecialTxNoContext(const CTransaction& tx, CValidationState& state)
546 : {
547 823776 : return CheckSpecialTx(tx, nullptr, nullptr, state);
548 : }
549 :
550 :
551 56616 : bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, const CCoinsViewCache* view, CValidationState& state, bool fJustCheck)
552 : {
553 56616 : AssertLockHeld(cs_main);
554 :
555 : // check special txes
556 604129 : for (const CTransactionRef& tx: block.vtx) {
557 547518 : if (!CheckSpecialTx(*tx, pindex->pprev, view, state)) {
558 : // pass the state returned by the function above
559 5 : return false;
560 : }
561 : }
562 :
563 56611 : if (!llmq::quorumBlockProcessor->ProcessBlock(block, pindex, state, fJustCheck)) {
564 : // pass the state returned by the function above
565 : return false;
566 : }
567 :
568 56607 : if (!deterministicMNManager->ProcessBlock(block, pindex, state, fJustCheck)) {
569 : // pass the state returned by the function above
570 12 : return false;
571 : }
572 :
573 : return true;
574 : }
575 :
576 1497 : bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex)
577 : {
578 1497 : if (!deterministicMNManager->UndoBlock(block, pindex)) {
579 : return false;
580 : }
581 1497 : if (!llmq::quorumBlockProcessor->UndoBlock(block, pindex)) {
582 0 : return false;
583 : }
584 : return true;
585 : }
586 :
587 1919 : uint256 CalcTxInputsHash(const CTransaction& tx)
588 : {
589 1919 : CHashWriter hw(CLIENT_VERSION, SER_GETHASH);
590 : // transparent inputs
591 3940 : for (const CTxIn& in: tx.vin) {
592 2021 : hw << in.prevout;
593 : }
594 : // shield inputs
595 1919 : if (tx.hasSaplingData()) {
596 0 : for (const SpendDescription& sd: tx.sapData->vShieldedSpend) {
597 0 : hw << sd.nullifier;
598 : }
599 : }
600 1919 : return hw.GetHash();
601 : }
602 :
603 : template <typename T>
604 1775 : bool GetValidatedTxPayload(const CTransaction& tx, T& obj, CValidationState& state)
605 : {
606 1775 : if (tx.nType != T::SPECIALTX_TYPE) {
607 0 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-type");
608 : }
609 1775 : if (!GetTxPayload(tx, obj)) {
610 3 : return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
611 : }
612 1774 : return obj.IsTriviallyValid(state);
613 : }
|