LCOV - code coverage report
Current view: top level - src/evo - specialtx_validation.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 197 229 86.0 %
Date: 2026-10-21 17:45:27 Functions: 26 26 100.0 %

          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             : }

Generated by: LCOV version 1.14