LCOV - code coverage report
Current view: top level - src/evo - specialtxman.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 433 720 60.1 %
Date: 2026-06-25 07:23:51 Functions: 26 29 89.7 %

          Line data    Source code
       1             : // Copyright (c) 2018-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             : #include <evo/specialtxman.h>
       6             : 
       7             : #include <chainlock/chainlock.h>
       8             : #include <chainlock/clsig.h>
       9             : #include <chainlock/handler.h>
      10             : #include <evo/assetlocktx.h>
      11             : #include <evo/cbtx.h>
      12             : #include <evo/creditpool.h>
      13             : #include <evo/deterministicmns.h>
      14             : #include <evo/mnhftx.h>
      15             : #include <evo/netinfo.h>
      16             : #include <evo/simplifiedmns.h>
      17             : #include <llmq/blockprocessor.h>
      18             : #include <llmq/commitment.h>
      19             : #include <llmq/quorumsman.h>
      20             : #include <llmq/utils.h>
      21             : #include <messagesigner.h>
      22             : #include <util/helpers.h>
      23             : 
      24             : #include <chainparams.h>
      25             : #include <consensus/amount.h>
      26             : #include <consensus/validation.h>
      27             : #include <deploymentstatus.h>
      28             : #include <hash.h>
      29             : #include <primitives/block.h>
      30             : #include <util/system.h>
      31             : #include <validation.h>
      32             : 
      33       18236 : static bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindex, const Consensus::Params& consensus_params,
      34             :                                    const CChain& chain, const llmq::CQuorumManager& qman,
      35             :                                    const chainlock::Chainlocks& chainlocks, BlockValidationState& state)
      36             : {
      37       18236 :     if (cbTx.nVersion < CCbTx::Version::CLSIG_AND_BALANCE) {
      38        3032 :         return true;
      39             :     }
      40             : 
      41       15204 :     static Mutex cached_mutex;
      42             :     static const CBlockIndex* cached_pindex GUARDED_BY(cached_mutex){nullptr};
      43       15204 :     static std::optional<std::pair<CBLSSignature, uint32_t>> cached_chainlock GUARDED_BY(cached_mutex){std::nullopt};
      44             : 
      45       15204 :     auto best_clsig = chainlocks.GetBestChainLock();
      46       15204 :     if (best_clsig.getHeight() == pindex->nHeight - 1 && cbTx.bestCLHeightDiff == 0 &&
      47           0 :         cbTx.bestCLSignature == best_clsig.getSig()) {
      48             :         // matches our best clsig which still hold values for the previous block
      49           0 :         LOCK(cached_mutex);
      50           0 :         cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
      51           0 :         cached_pindex = pindex;
      52           0 :         return true;
      53           0 :     }
      54             : 
      55       15204 :     std::optional<std::pair<CBLSSignature, uint32_t>> prevBlockCoinbaseChainlock{std::nullopt};
      56       15204 :     if (LOCK(cached_mutex); cached_pindex == pindex->pprev) {
      57           0 :         prevBlockCoinbaseChainlock = cached_chainlock;
      58           0 :     }
      59       15204 :     if (!prevBlockCoinbaseChainlock.has_value()) {
      60       15204 :         prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindex->pprev);
      61       15204 :     }
      62             :     // If std::optional prevBlockCoinbaseChainlock is empty, then up to the previous block, coinbase Chainlock is null.
      63       15204 :     if (prevBlockCoinbaseChainlock.has_value()) {
      64             :         // Previous block Coinbase has a non-null Chainlock: current block's Chainlock must be non-null and at least as new as the previous one
      65           0 :         if (!cbTx.bestCLSignature.IsValid()) {
      66             :             // IsNull() doesn't exist for CBLSSignature: we assume that a non valid BLS sig is null
      67           0 :             return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-null-clsig");
      68             :         }
      69           0 :         if (cbTx.bestCLHeightDiff > prevBlockCoinbaseChainlock.value().second + 1) {
      70           0 :             return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-older-clsig");
      71             :         }
      72           0 :     }
      73             : 
      74             :     // IsNull() doesn't exist for CBLSSignature: we assume that a valid BLS sig is non-null
      75       15204 :     if (cbTx.bestCLSignature.IsValid()) {
      76           0 :         int curBlockCoinbaseCLHeight = pindex->nHeight - static_cast<int>(cbTx.bestCLHeightDiff) - 1;
      77           0 :         if (best_clsig.getHeight() == curBlockCoinbaseCLHeight && best_clsig.getSig() == cbTx.bestCLSignature) {
      78             :             // matches our best (but outdated) clsig, no need to verify it again
      79           0 :             LOCK(cached_mutex);
      80           0 :             cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
      81           0 :             cached_pindex = pindex;
      82           0 :             return true;
      83           0 :         }
      84           0 :         uint256 curBlockCoinbaseCLBlockHash = pindex->GetAncestor(curBlockCoinbaseCLHeight)->GetBlockHash();
      85           0 :         chainlock::ChainLockSig clsig{curBlockCoinbaseCLHeight, curBlockCoinbaseCLBlockHash, cbTx.bestCLSignature};
      86           0 :         llmq::VerifyRecSigStatus ret = chainlock::VerifyChainLock(consensus_params, chain, qman, clsig);
      87           0 :         if (ret != llmq::VerifyRecSigStatus::Valid) {
      88           0 :             return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-invalid-clsig");
      89             :         }
      90           0 :         LOCK(cached_mutex);
      91           0 :         cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
      92           0 :         cached_pindex = pindex;
      93       15204 :     } else if (cbTx.bestCLHeightDiff != 0) {
      94             :         // Null bestCLSignature is allowed only with bestCLHeightDiff = 0
      95           0 :         return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-cldiff");
      96             :     }
      97             : 
      98       15204 :     return true;
      99       18236 : }
     100             : 
     101       61022 : static bool CheckSpecialTxInner(CDeterministicMNManager& dmnman, llmq::CQuorumSnapshotManager& qsnapman,
     102             :                                 const ChainstateManager& chainman, const llmq::CQuorumManager& qman,
     103             :                                 const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache& view,
     104             :                                 const std::optional<CRangesSet>& indexes, bool check_sigs, TxValidationState& state)
     105             :     EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
     106             : {
     107       61022 :     AssertLockHeld(::cs_main);
     108             : 
     109       61022 :     if (!tx.HasExtraPayloadField())
     110       31925 :         return true;
     111             : 
     112       29097 :     if (!DeploymentActiveAfter(pindexPrev, chainman.GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) {
     113           1 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-tx-type-dip3-inactive");
     114             :     }
     115             : 
     116             :     try {
     117       29096 :         switch (tx.nType) {
     118             :         case TRANSACTION_PROVIDER_REGISTER:
     119          35 :             return CheckProRegTx(tx, pindexPrev, dmnman, view, chainman, state, check_sigs);
     120             :         case TRANSACTION_PROVIDER_UPDATE_SERVICE:
     121           4 :             return CheckProUpServTx(tx, pindexPrev, dmnman, chainman, state, check_sigs);
     122             :         case TRANSACTION_PROVIDER_UPDATE_REGISTRAR:
     123           3 :             return CheckProUpRegTx(tx, pindexPrev, dmnman, view, chainman, state, check_sigs);
     124             :         case TRANSACTION_PROVIDER_UPDATE_REVOKE:
     125           4 :             return CheckProUpRevTx(tx, pindexPrev, dmnman, chainman, state, check_sigs);
     126             :         case TRANSACTION_COINBASE: {
     127           0 :             if (!tx.IsCoinBase()) {
     128           0 :                 return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-invalid");
     129             :             }
     130           0 :             if (const auto opt_cbTx = GetTxPayload<CCbTx>(tx)) {
     131           0 :                 return CheckCbTx(*opt_cbTx, pindexPrev, state);
     132             :             } else {
     133           0 :                 return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-payload");
     134             :             }
     135             :         }
     136             :         case TRANSACTION_QUORUM_COMMITMENT:
     137       29050 :             return llmq::CheckLLMQCommitment({dmnman, qsnapman, chainman, pindexPrev}, tx, state);
     138             :         case TRANSACTION_MNHF_SIGNAL:
     139           0 :             return CheckMNHFTx(chainman, qman, tx, pindexPrev, state);
     140             :         case TRANSACTION_ASSET_LOCK:
     141           0 :             return CheckAssetLockTx(tx, state);
     142             :         case TRANSACTION_ASSET_UNLOCK:
     143           0 :             return CheckAssetUnlockTx(chainman.m_blockman, qman, tx, pindexPrev, indexes, state);
     144             :         }
     145           0 :     } catch (const std::exception& e) {
     146           0 :         LogPrintf("%s -- failed: %s\n", __func__, e.what());
     147           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "failed-check-special-tx");
     148           0 :     }
     149             : 
     150           0 :     return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-tx-type-check");
     151       61022 : }
     152             : 
     153          84 : bool CSpecialTxProcessor::CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache& view, bool check_sigs, TxValidationState& state)
     154             : {
     155          84 :     AssertLockHeld(::cs_main);
     156         168 :     return CheckSpecialTxInner(m_dmnman, m_qsnapman, m_chainman, m_qman, tx, pindexPrev, view, std::nullopt, check_sigs,
     157          84 :                                state);
     158           0 : }
     159             : 
     160           0 : static void HandleQuorumCommitment(const llmq::CFinalCommitment& qc, const std::vector<CDeterministicMNCPtr>& members,
     161             :                                    bool debugLogs, CDeterministicMNList& mnList)
     162             : {
     163           0 :     for (size_t i = 0; i < members.size(); i++) {
     164           0 :         if (!mnList.HasMN(members[i]->proTxHash)) {
     165           0 :             continue;
     166             :         }
     167           0 :         if (!qc.validMembers[i]) {
     168             :             // punish MN for failed DKG participation
     169             :             // The idea is to immediately ban a MN when it fails 2 DKG sessions with only a few blocks in-between
     170             :             // If there were enough blocks between failures, the MN has a chance to recover as he reduces his penalty by 1 for every block
     171             :             // If it however fails 3 times in the timespan of a single payment cycle, it should definitely get banned
     172           0 :             mnList.PoSePunish(members[i]->proTxHash, mnList.CalcPenalty(66), debugLogs);
     173           0 :         }
     174           0 :     }
     175           0 : }
     176             : 
     177       36470 : bool CSpecialTxProcessor::BuildNewListFromBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindexPrev,
     178             :                                                 const CCoinsViewCache& view, bool debugLogs,
     179             :                                                 BlockValidationState& state, CDeterministicMNList& mnListRet)
     180             : {
     181       36470 :     AssertLockHeld(cs_main);
     182       36470 :     CDeterministicMNList oldList = m_dmnman.GetListForBlock(pindexPrev);
     183       36470 :     return RebuildListFromBlock(block, pindexPrev, oldList, view, debugLogs, state, mnListRet);
     184       36470 : }
     185             : 
     186       36470 : bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindexPrev,
     187             :                                                 const CDeterministicMNList& prevList, const CCoinsViewCache& view,
     188             :                                                 bool debugLogs, BlockValidationState& state,
     189             :                                                 CDeterministicMNList& mnListRet)
     190             : {
     191             :     // Verify that prevList either represents an empty/initial state (default-constructed),
     192             :     // or it matches the previous block's hash.
     193       36470 :     assert(prevList == CDeterministicMNList() || prevList.GetBlockHash() == pindexPrev->GetBlockHash());
     194             : 
     195       36470 :     int nHeight = pindexPrev->nHeight + 1;
     196             : 
     197       36470 :     CDeterministicMNList newList = prevList;
     198       36470 :     newList.SetBlockHash(uint256()); // we can't know the final block hash, so better not return a (invalid) block hash
     199       36470 :     newList.SetHeight(nHeight);
     200             : 
     201       36470 :     auto payee = prevList.GetMNPayee(pindexPrev);
     202             : 
     203             :     // we iterate the prevList here and update the newList
     204             :     // this is only valid as long these have not diverged at this point, which is the case as long as we don't add
     205             :     // code above this loop that modifies newList
     206       50691 :     prevList.ForEachMN(/*onlyValid=*/false, [&pindexPrev, &newList, this](const auto& dmn) {
     207       14221 :         if (!dmn.pdmnState->confirmedHash.IsNull()) {
     208             :             // already confirmed
     209       13958 :             return;
     210             :         }
     211             :         // this works on the previous block, so confirmation will happen one block after nMasternodeMinimumConfirmations
     212             :         // has been reached, but the block hash will then point to the block at nMasternodeMinimumConfirmations
     213         263 :         int nConfirmations = pindexPrev->nHeight - dmn.pdmnState->nRegisteredHeight;
     214         263 :         if (nConfirmations >= this->m_consensus_params.nMasternodeMinimumConfirmations) {
     215         130 :             auto newState = std::make_shared<CDeterministicMNState>(*dmn.pdmnState);
     216         130 :             newState->UpdateConfirmedHash(dmn.proTxHash, pindexPrev->GetBlockHash());
     217         130 :             newList.UpdateMN(dmn.proTxHash, newState);
     218         130 :         }
     219       14221 :     });
     220             : 
     221       36470 :     newList.DecreaseScores();
     222             : 
     223       36470 :     const bool isMNRewardReallocation{
     224       36470 :         DeploymentActiveAfter(pindexPrev, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_MN_RR)};
     225       36470 :     const bool is_v24_deployed{DeploymentActiveAfter(pindexPrev, m_chainman, Consensus::DEPLOYMENT_V24)};
     226             : 
     227             :     // we skip the coinbase
     228       94668 :     for (int i = 1; i < (int)block.vtx.size(); i++) {
     229       58198 :         const CTransaction& tx = *block.vtx[i];
     230             : 
     231       58198 :         if (!tx.IsSpecialTxVersion()) {
     232             :             // only interested in special TXs
     233           8 :             continue;
     234             :         }
     235             : 
     236       58190 :         if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
     237          69 :             const auto opt_proTx = GetTxPayload<CProRegTx>(tx);
     238          69 :             if (!opt_proTx) {
     239           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
     240             :             }
     241          69 :             auto& proTx = *opt_proTx;
     242             : 
     243          69 :             auto dmn = std::make_shared<CDeterministicMN>(newList.GetTotalRegisteredCount(), proTx.nType);
     244          69 :             dmn->proTxHash = tx.GetHash();
     245             : 
     246             :             // collateralOutpoint is either pointing to an external collateral or to the ProRegTx itself
     247          69 :             if (proTx.collateralOutpoint.hash.IsNull()) {
     248          66 :                 dmn->collateralOutpoint = COutPoint(tx.GetHash(), proTx.collateralOutpoint.n);
     249          66 :             } else {
     250           3 :                 dmn->collateralOutpoint = proTx.collateralOutpoint;
     251             :             }
     252             : 
     253             :             // Complain about spent collaterals only when we process the tip.
     254             :             // This is safe because blocks below the tip were verified
     255             :             // when they were connected initially.
     256          69 :             if (!view.GetBestBlock().IsNull()) {
     257          69 :                 Coin coin;
     258          69 :                 CAmount expectedCollateral = GetMnType(proTx.nType).collat_amount;
     259          72 :                 if (!proTx.collateralOutpoint.hash.IsNull() && (!view.GetCoin(dmn->collateralOutpoint, coin) ||
     260           3 :                                                                 coin.IsSpent() || coin.out.nValue != expectedCollateral)) {
     261             :                     // should actually never get to this point as CheckProRegTx should have handled this case.
     262             :                     // We do this additional check nevertheless to be 100% sure
     263           0 :                     return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-collateral");
     264             :                 }
     265          69 :             }
     266             : 
     267          69 :             auto replacedDmn = newList.GetMNByCollateral(dmn->collateralOutpoint);
     268          69 :             if (replacedDmn != nullptr) {
     269             :                 // This might only happen with a ProRegTx that refers an external collateral
     270             :                 // In that case the new ProRegTx will replace the old one. This means the old one is removed
     271             :                 // and the new one is added like a completely fresh one, which is also at the bottom of the payment list
     272           0 :                 newList.RemoveMN(replacedDmn->proTxHash);
     273           0 :                 if (debugLogs) {
     274           0 :                     LogPrintf("%s -- MN %s removed from list because collateral was used for " /* Continued */
     275             :                               "a new ProRegTx. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n",
     276             :                               __func__, replacedDmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(),
     277             :                               nHeight, newList.GetCounts().total());
     278           0 :                 }
     279           0 :             }
     280             : 
     281         138 :             for (const auto& entry : proTx.netInfo->GetEntries()) {
     282         138 :                 if (const auto service_opt{entry.GetAddrPort()}) {
     283          69 :                     if (newList.HasUniqueProperty(*service_opt)) {
     284           0 :                         return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
     285             :                     }
     286          69 :                 } else if (const auto domain_opt{entry.GetDomainPort()}) {
     287           0 :                     if (newList.HasUniqueProperty(*domain_opt)) {
     288           0 :                         return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
     289             :                     }
     290           0 :                 } else {
     291           0 :                     return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-netinfo-entry");
     292             :                 }
     293             :             }
     294          69 :             if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.pubKeyOperator)) {
     295           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-key");
     296             :             }
     297             : 
     298          69 :             dmn->nOperatorReward = proTx.nOperatorReward;
     299             : 
     300          69 :             auto dmnState = std::make_shared<CDeterministicMNState>(proTx);
     301          69 :             dmnState->nRegisteredHeight = nHeight;
     302          69 :             if (proTx.netInfo->IsEmpty()) {
     303             :                 // start in banned pdmnState as we need to wait for a ProUpServTx
     304           0 :                 dmnState->BanIfNotBanned(nHeight);
     305           0 :             }
     306          69 :             dmn->pdmnState = dmnState;
     307             : 
     308          69 :             newList.AddMN(dmn);
     309             : 
     310          69 :             if (debugLogs) {
     311          69 :                 LogPrintf("%s -- MN %s added at height %d: %s\n", __func__, tx.GetHash().ToString(), nHeight,
     312             :                           proTx.ToString());
     313          69 :             }
     314       58190 :         } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
     315           8 :             const auto opt_proTx = GetTxPayload<CProUpServTx>(tx);
     316           8 :             if (!opt_proTx) {
     317           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
     318             :             }
     319             : 
     320          16 :             for (const auto& entry : opt_proTx->netInfo->GetEntries()) {
     321          16 :                 if (const auto service_opt{entry.GetAddrPort()}) {
     322           8 :                     if (newList.HasUniqueProperty(*service_opt) &&
     323           0 :                         newList.GetUniquePropertyMN(*service_opt)->proTxHash != opt_proTx->proTxHash) {
     324           0 :                         return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
     325             :                     }
     326           8 :                 } else if (const auto domain_opt{entry.GetDomainPort()}) {
     327           0 :                     if (newList.HasUniqueProperty(*domain_opt) &&
     328           0 :                         newList.GetUniquePropertyMN(*domain_opt)->proTxHash != opt_proTx->proTxHash) {
     329           0 :                         return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
     330             :                     }
     331           0 :                 } else {
     332           0 :                     return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-netinfo-entry");
     333             :                 }
     334             :             }
     335             : 
     336           8 :             auto dmn = newList.GetMN(opt_proTx->proTxHash);
     337           8 :             if (!dmn) {
     338           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash");
     339             :             }
     340           8 :             if (opt_proTx->nType != dmn->nType) {
     341           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-type-mismatch");
     342             :             }
     343           8 :             if (!IsValidMnType(opt_proTx->nType)) {
     344           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-type");
     345             :             }
     346             : 
     347           8 :             auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
     348           8 :             if (is_v24_deployed) {
     349             :                 // Extended addresses support in v24 means that the version can be updated
     350           0 :                 newState->nVersion = opt_proTx->nVersion;
     351           0 :             }
     352           8 :             newState->netInfo = opt_proTx->netInfo;
     353           8 :             newState->scriptOperatorPayout = opt_proTx->scriptOperatorPayout;
     354           8 :             if (opt_proTx->nType == MnType::Evo) {
     355           0 :                 newState->platformNodeID = opt_proTx->platformNodeID;
     356           0 :                 if (opt_proTx->nVersion < ProTxVersion::ExtAddr) {
     357           0 :                     newState->platformP2PPort = opt_proTx->platformP2PPort;
     358           0 :                     newState->platformHTTPPort = opt_proTx->platformHTTPPort;
     359           0 :                 } else {
     360             :                     // From ExtAddr onwards the Platform ports are stored in netInfo. Clear the
     361             :                     // legacy scalar fields (which a legacy registration may have left set) so the
     362             :                     // in-memory state matches its serialized form, which omits them for ExtAddr
     363             :                     // (see CDeterministicMNState serialization). Otherwise a stale value would
     364             :                     // survive in diff-reconstructed lists but vanish through a snapshot round-trip.
     365           0 :                     newState->platformP2PPort = 0;
     366           0 :                     newState->platformHTTPPort = 0;
     367             :                 }
     368           0 :             }
     369           8 :             if (newState->IsBanned()) {
     370             :                 // only revive when all keys are set
     371           8 :                 if (newState->pubKeyOperator != CBLSLazyPublicKey() && !newState->keyIDVoting.IsNull() &&
     372           4 :                     !newState->keyIDOwner.IsNull()) {
     373           4 :                     newState->Revive(nHeight);
     374           4 :                     if (debugLogs) {
     375           4 :                         LogPrintf("%s -- MN %s revived at height %d\n", __func__, opt_proTx->proTxHash.ToString(), nHeight);
     376           4 :                     }
     377           4 :                 }
     378           4 :             }
     379             : 
     380           8 :             newList.UpdateMN(opt_proTx->proTxHash, newState);
     381           8 :             if (debugLogs) {
     382           8 :                 LogPrintf("%s -- MN %s updated at height %d: %s\n", __func__, opt_proTx->proTxHash.ToString(), nHeight,
     383             :                           opt_proTx->ToString());
     384           8 :             }
     385       58121 :         } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
     386           6 :             const auto opt_proTx = GetTxPayload<CProUpRegTx>(tx);
     387           6 :             if (!opt_proTx) {
     388           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
     389             :             }
     390             : 
     391           6 :             auto dmn = newList.GetMN(opt_proTx->proTxHash);
     392           6 :             if (!dmn) {
     393           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash");
     394             :             }
     395           6 :             auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
     396           6 :             if (newState->pubKeyOperator != opt_proTx->pubKeyOperator) {
     397             :                 // reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
     398           6 :                 newState->ResetOperatorFields();
     399           6 :                 newState->BanIfNotBanned(nHeight);
     400             :                 // we update pubKeyOperator here, make sure state version matches
     401             :                 // Make sure we don't accidentally downgrade the state version if using version after basic BLS
     402           6 :                 newState->nVersion = newState->nVersion > ProTxVersion::BasicBLS ? newState->nVersion : opt_proTx->nVersion;
     403           6 :                 newState->netInfo = NetInfoInterface::MakeNetInfo(newState->nVersion);
     404           6 :                 newState->pubKeyOperator = opt_proTx->pubKeyOperator;
     405           6 :             }
     406           6 :             newState->keyIDVoting = opt_proTx->keyIDVoting;
     407           6 :             newState->scriptPayout = opt_proTx->scriptPayout;
     408             : 
     409           6 :             newList.UpdateMN(opt_proTx->proTxHash, newState);
     410             : 
     411           6 :             if (debugLogs) {
     412           6 :                 LogPrintf("%s -- MN %s updated at height %d: %s\n", __func__, opt_proTx->proTxHash.ToString(), nHeight,
     413             :                           opt_proTx->ToString());
     414           6 :             }
     415       58113 :         } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) {
     416           7 :             const auto opt_proTx = GetTxPayload<CProUpRevTx>(tx);
     417           7 :             if (!opt_proTx) {
     418           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
     419             :             }
     420             : 
     421           7 :             auto dmn = newList.GetMN(opt_proTx->proTxHash);
     422           7 :             if (!dmn) {
     423           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash");
     424             :             }
     425           7 :             auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
     426           7 :             newState->ResetOperatorFields();
     427           7 :             newState->BanIfNotBanned(nHeight);
     428           7 :             newState->nRevocationReason = opt_proTx->nReason;
     429             : 
     430           7 :             newList.UpdateMN(opt_proTx->proTxHash, newState);
     431             : 
     432           7 :             if (debugLogs) {
     433           7 :                 LogPrintf("%s -- MN %s revoked operator key at height %d: %s\n", __func__,
     434             :                           opt_proTx->proTxHash.ToString(), nHeight, opt_proTx->ToString());
     435           7 :             }
     436       58107 :         } else if (tx.nType == TRANSACTION_QUORUM_COMMITMENT) {
     437       58100 :             const auto opt_qc = GetTxPayload<llmq::CFinalCommitmentTxPayload>(tx);
     438       58100 :             if (!opt_qc) {
     439           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-payload");
     440             :             }
     441       58100 :             if (!opt_qc->commitment.IsNull()) {
     442           0 :                 const auto& llmq_params_opt = Params().GetLLMQ(opt_qc->commitment.llmqType);
     443           0 :                 if (!llmq_params_opt.has_value()) {
     444           0 :                     return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-commitment-type");
     445             :                 }
     446           0 :                 int qcnHeight = int(opt_qc->nHeight);
     447           0 :                 int quorumHeight = qcnHeight - (qcnHeight % llmq_params_opt->dkgInterval) +
     448           0 :                                    int(opt_qc->commitment.quorumIndex);
     449           0 :                 auto pQuorumBaseBlockIndex = pindexPrev->GetAncestor(quorumHeight);
     450           0 :                 if (!pQuorumBaseBlockIndex || pQuorumBaseBlockIndex->GetBlockHash() != opt_qc->commitment.quorumHash) {
     451             :                     // we should actually never get into this case as validation should have caught it...but let's be sure
     452           0 :                     return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-quorum-hash");
     453             :                 }
     454             : 
     455             :                 // The commitment has already been validated at this point, so it's safe to use members of it
     456             : 
     457           0 :                 const auto members = llmq::utils::GetAllQuorumMembers(opt_qc->commitment.llmqType,
     458           0 :                                                                       {m_dmnman, m_qsnapman, m_chainman,
     459           0 :                                                                        pQuorumBaseBlockIndex});
     460           0 :                 HandleQuorumCommitment(opt_qc->commitment, members, debugLogs, newList);
     461           0 :             }
     462       58100 :         }
     463       58190 :     }
     464             : 
     465             :     // we skip the coinbase
     466       94668 :     for (int i = 1; i < (int)block.vtx.size(); i++) {
     467       58198 :         const CTransaction& tx = *block.vtx[i];
     468             : 
     469             :         // check if any existing MN collateral is spent by this transaction
     470       58420 :         for (const auto& in : tx.vin) {
     471         222 :             auto dmn = newList.GetMNByCollateral(in.prevout);
     472         222 :             if (dmn && dmn->collateralOutpoint == in.prevout) {
     473           5 :                 newList.RemoveMN(dmn->proTxHash);
     474             : 
     475           5 :                 if (debugLogs) {
     476           5 :                     LogPrintf("%s -- MN %s removed from list because collateral was spent. " /* Continued */
     477             :                               "collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n",
     478             :                               __func__, dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight,
     479             :                               newList.GetCounts().total());
     480           5 :                 }
     481           5 :             }
     482         222 :         }
     483       58198 :     }
     484             : 
     485             :     // The payee for the current block was determined by the previous block's list, but it might have disappeared in the
     486             :     // current block. We still pay that MN one last time, however.
     487       44644 :     if (auto dmn = payee ? newList.GetMN(payee->proTxHash) : nullptr) {
     488        8174 :         auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
     489        8174 :         newState->nLastPaidHeight = nHeight;
     490             :         // Starting from v19 and until MNRewardReallocation, EvoNodes will be paid 4 blocks in a row
     491             :         // No need to check if v19 is active, since EvoNode ProRegTxes are allowed only after v19 activation
     492             :         // Note: If the payee wasn't found in the current block that's fine
     493        8174 :         if (dmn->nType == MnType::Evo && !isMNRewardReallocation) {
     494           0 :             ++newState->nConsecutivePayments;
     495           0 :             if (debugLogs) {
     496           0 :                 LogPrint(BCLog::MNPAYMENTS, "%s -- MN %s is an EvoNode, bumping nConsecutivePayments to %d\n", __func__,
     497             :                          dmn->proTxHash.ToString(), newState->nConsecutivePayments);
     498           0 :             }
     499           0 :         }
     500        8174 :         newList.UpdateMN(payee->proTxHash, newState);
     501        8174 :     }
     502             : 
     503             :     // reset nConsecutivePayments on non-paid EvoNodes
     504       36470 :     auto newList2 = newList;
     505       50755 :     newList2.ForEachMN(/*onlyValid=*/false, [&](const auto& dmn) {
     506       14285 :         if (dmn.nType != MnType::Evo) return;
     507           0 :         if (payee != nullptr && dmn.proTxHash == payee->proTxHash && !isMNRewardReallocation) return;
     508           0 :         if (dmn.pdmnState->nConsecutivePayments == 0) return;
     509           0 :         if (debugLogs) {
     510           0 :             LogPrint(BCLog::MNPAYMENTS, "%s -- MN %s, reset nConsecutivePayments %d->0\n", __func__,
     511             :                      dmn.proTxHash.ToString(), dmn.pdmnState->nConsecutivePayments);
     512           0 :         }
     513           0 :         auto newState = std::make_shared<CDeterministicMNState>(*dmn.pdmnState);
     514           0 :         newState->nConsecutivePayments = 0;
     515           0 :         newList.UpdateMN(dmn.proTxHash, newState);
     516       14285 :     });
     517             : 
     518       36470 :     mnListRet = newList;
     519             : 
     520       36470 :     return true;
     521       36470 : }
     522             : 
     523       49015 : bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, const CCoinsViewCache& view, bool fJustCheck,
     524             :                                                    bool fCheckCbTxMerkleRoots, BlockValidationState& state, std::optional<MNListUpdates>& updatesRet)
     525             : {
     526       49015 :     AssertLockHeld(::cs_main);
     527             : 
     528             :     try {
     529             :         static int64_t nTimeLoop = 0;
     530             :         static int64_t nTimeQuorum = 0;
     531             :         static int64_t nTimeDMN = 0;
     532             :         static int64_t nTimeMerkleMNL = 0;
     533             :         static int64_t nTimeMerkleQuorums = 0;
     534             :         static int64_t nTimeCbTxCL = 0;
     535             :         static int64_t nTimeMnehf = 0;
     536             :         static int64_t nTimePayload = 0;
     537             :         static int64_t nTimeCreditPool = 0;
     538             : 
     539       49015 :         int64_t nTime1 = GetTimeMicros();
     540             : 
     541       49015 :         std::optional<CCbTx> opt_cbTx{std::nullopt};
     542       49015 :         if (fCheckCbTxMerkleRoots && block.vtx.size() > 0 && block.vtx[0]->nType == TRANSACTION_COINBASE) {
     543       18236 :             const auto& tx = block.vtx[0];
     544       18236 :             if (!tx->IsCoinBase()) {
     545           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-invalid");
     546             :             }
     547       18236 :             if (opt_cbTx = GetTxPayload<CCbTx>(*tx); opt_cbTx) {
     548       18236 :                 TxValidationState tx_state;
     549       18236 :                 if (!CheckCbTx(*opt_cbTx, pindex->pprev, tx_state)) {
     550           0 :                     assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS ||
     551             :                            tx_state.GetResult() == TxValidationResult::TX_BAD_SPECIAL);
     552           0 :                     return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(),
     553           0 :                                          strprintf("Special Transaction check failed (tx hash %s) %s",
     554           0 :                                                    tx->GetHash().ToString(), tx_state.GetDebugMessage()));
     555             :                 }
     556       18236 :             } else {
     557           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-payload");
     558             :             }
     559       18236 :         }
     560       49015 :         if (fCheckCbTxMerkleRoots) {
     561             :             // To ensure that opt_cbTx is not missing when it's supposed to be
     562       49015 :             if (DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_DIP0003) && !opt_cbTx.has_value()) {
     563           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-version");
     564             :             }
     565       49015 :         }
     566             : 
     567       49015 :         int64_t nTime2 = GetTimeMicros();
     568       49015 :         nTimePayload += nTime2 - nTime1;
     569       49015 :         LogPrint(BCLog::BENCHMARK, "      - GetTxPayload: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1),
     570             :                  nTimePayload * 0.000001);
     571             : 
     572       49015 :         CRangesSet indexes;
     573       49015 :         if (DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_V20)) {
     574       15204 :             CCreditPool creditPool{m_cpoolman.GetCreditPool(pindex->pprev)};
     575       15204 :             LogPrint(BCLog::CREDITPOOL, "CSpecialTxProcessor::%s -- CCreditPool is %s\n", __func__, creditPool.ToString());
     576       15204 :             indexes = std::move(creditPool.indexes);
     577       15204 :         }
     578             : 
     579      128188 :         for (size_t i = 0; i < block.vtx.size(); ++i) {
     580             :             // we validated CCbTx above, starts from the 2nd transaction
     581       79174 :             if (i == 0 && block.vtx[i]->nType == TRANSACTION_COINBASE) continue;
     582             : 
     583       60938 :             const auto ptr_tx = block.vtx[i];
     584       60938 :             TxValidationState tx_state;
     585             :             // At this moment CheckSpecialTx() may fail by 2 possible ways:
     586             :             // consensus failures and "TX_BAD_SPECIAL"
     587       60938 :             if (!CheckSpecialTxInner(m_dmnman, m_qsnapman, m_chainman, m_qman, *ptr_tx, pindex->pprev, view, indexes,
     588       60938 :                                      fCheckCbTxMerkleRoots, tx_state)) {
     589           1 :                 assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS || tx_state.GetResult() == TxValidationResult::TX_BAD_SPECIAL);
     590           2 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(),
     591           1 :                                  strprintf("Special Transaction check failed (tx hash %s) %s", ptr_tx->GetHash().ToString(), tx_state.GetDebugMessage()));
     592             :             }
     593       60938 :         }
     594             : 
     595       49014 :         int64_t nTime3 = GetTimeMicros();
     596       49014 :         nTimeLoop += nTime3 - nTime2;
     597       49014 :         LogPrint(BCLog::BENCHMARK, "      - Loop: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeLoop * 0.000001);
     598             : 
     599       49014 :         if (opt_cbTx.has_value()) {
     600       18236 :             if (!CheckCreditPoolDiffForBlock(block, pindex, *opt_cbTx, state)) {
     601           0 :                 return error("CSpecialTxProcessor: CheckCreditPoolDiffForBlock for block %s failed with %s",
     602           0 :                              pindex->GetBlockHash().ToString(), state.ToString());
     603             :             }
     604       18236 :         }
     605             : 
     606       49014 :         int64_t nTime4 = GetTimeMicros();
     607       49014 :         nTimeCreditPool += nTime4 - nTime3;
     608       49014 :         LogPrint(BCLog::BENCHMARK, "      - CheckCreditPoolDiffForBlock: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3),
     609             :                  nTimeCreditPool * 0.000001);
     610             : 
     611       49014 :         if (!m_qblockman.ProcessBlock(block, pindex, state, fJustCheck, fCheckCbTxMerkleRoots)) {
     612             :             // pass the state returned by the function above
     613           0 :             return false;
     614             :         }
     615             : 
     616       49014 :         int64_t nTime5 = GetTimeMicros();
     617       49014 :         nTimeQuorum += nTime5 - nTime4;
     618       49014 :         LogPrint(BCLog::BENCHMARK, "      - m_qblockman.ProcessBlock: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4),
     619             :                  nTimeQuorum * 0.000001);
     620             : 
     621       49014 :         CDeterministicMNList mn_list;
     622       49014 :         if (DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_DIP0003)) {
     623       18236 :             if (!BuildNewListFromBlock(block, pindex->pprev, view, true, state, mn_list)) {
     624             :                 // pass the state returned by the function above
     625           0 :                 return false;
     626             :             }
     627       18236 :             mn_list.SetBlockHash(pindex->GetBlockHash());
     628             : 
     629       18236 :             if (!fJustCheck && !m_dmnman.ProcessBlock(block, pindex, state, mn_list, updatesRet)) {
     630             :                 // pass the state returned by the function above
     631           0 :                 return false;
     632             :             }
     633       18236 :         }
     634             : 
     635       49014 :         int64_t nTime6 = GetTimeMicros();
     636       49014 :         nTimeDMN += nTime6 - nTime5;
     637       49014 :         LogPrint(BCLog::BENCHMARK, "      - m_dmnman.ProcessBlock: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5),
     638             :                  nTimeDMN * 0.000001);
     639             : 
     640       49014 :         if (opt_cbTx.has_value()) {
     641       18236 :             uint256 calculatedMerkleRootMNL;
     642       18236 :             if (!CalcCbTxMerkleRootMNList(calculatedMerkleRootMNL, mn_list.to_sml(), state)) {
     643             :                 // pass the state returned by the function above
     644           0 :                 return false;
     645             :             }
     646       18236 :             if (calculatedMerkleRootMNL != opt_cbTx->merkleRootMNList) {
     647           0 :                 return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-mnmerkleroot");
     648             :             }
     649             : 
     650       18236 :             int64_t nTime6_1 = GetTimeMicros();
     651       18236 :             nTimeMerkleMNL += nTime6_1 - nTime6;
     652       18236 :             LogPrint(BCLog::BENCHMARK, "      - CalcCbTxMerkleRootMNList: %.2fms [%.2fs]\n",
     653             :                      0.001 * (nTime6_1 - nTime6), nTimeMerkleMNL * 0.000001);
     654             : 
     655       18236 :             if (opt_cbTx->nVersion >= CCbTx::Version::MERKLE_ROOT_QUORUMS) {
     656       18236 :                 uint256 calculatedMerkleRootQuorums;
     657       18236 :                 if (!CalcCbTxMerkleRootQuorums(block, pindex->pprev, m_qblockman, calculatedMerkleRootQuorums, state)) {
     658             :                     // pass the state returned by the function above
     659           0 :                     return false;
     660             :                 }
     661       18236 :                 if (calculatedMerkleRootQuorums != opt_cbTx->merkleRootQuorums) {
     662           0 :                     return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-quorummerkleroot");
     663             :                 }
     664       18236 :             }
     665             : 
     666       18236 :             int64_t nTime6_2 = GetTimeMicros();
     667       18236 :             nTimeMerkleQuorums += nTime6_2 - nTime6_1;
     668             : 
     669       18236 :             LogPrint(BCLog::BENCHMARK, "      - CalcCbTxMerkleRootQuorums: %.2fms [%.2fs]\n",
     670             :                      0.001 * (nTime6_2 - nTime6_1), nTimeMerkleQuorums * 0.000001);
     671             : 
     672       18236 :             if (!CheckCbTxBestChainlock(*opt_cbTx, pindex, m_consensus_params, m_chainman.ActiveChain(), m_qman,
     673       18236 :                                         m_chainlocks, state)) {
     674             :                 // pass the state returned by the function above
     675           0 :                 return false;
     676             :             }
     677             : 
     678       18236 :             int64_t nTime6_3 = GetTimeMicros();
     679       18236 :             nTimeCbTxCL += nTime6_3 - nTime6_2;
     680       18236 :             LogPrint(BCLog::BENCHMARK, "      - CheckCbTxBestChainlock: %.2fms [%.2fs]\n",
     681             :                      0.001 * (nTime6_3 - nTime6_2), nTimeCbTxCL * 0.000001);
     682       18236 :         }
     683             : 
     684       49014 :         int64_t nTime7 = GetTimeMicros();
     685             : 
     686       49014 :         if (!m_mnhfman.ProcessBlock(block, pindex, fJustCheck, state)) {
     687             :             // pass the state returned by the function above
     688           0 :             return false;
     689             :         }
     690             : 
     691       49014 :         int64_t nTime8 = GetTimeMicros();
     692       49014 :         nTimeMnehf += nTime8 - nTime7;
     693       49014 :         LogPrint(BCLog::BENCHMARK, "      - m_mnhfman.ProcessBlock: %.2fms [%.2fs]\n", 0.001 * (nTime8 - nTime7),
     694             :                  nTimeMnehf * 0.000001);
     695             : 
     696       49014 :         if (DeploymentActiveAfter(pindex, m_consensus_params, Consensus::DEPLOYMENT_V19) && bls::bls_legacy_scheme.load()) {
     697             :             // NOTE: The block next to the activation is the one that is using new rules.
     698             :             // V19 activated just activated, so we must switch to the new rules here.
     699         204 :             bls::bls_legacy_scheme.store(false);
     700         204 :             LogPrintf("CSpecialTxProcessor::%s -- bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
     701         204 :         }
     702       49015 :     } catch (const std::exception& e) {
     703           0 :         LogPrintf("CSpecialTxProcessor::%s -- FAILURE! %s\n", __func__, e.what());
     704           0 :         return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-procspectxsinblock");
     705           0 :     }
     706             : 
     707       49014 :     return true;
     708       49015 : }
     709             : 
     710         389 : bool CSpecialTxProcessor::UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, std::optional<MNListUpdates>& updatesRet)
     711             : {
     712         389 :     AssertLockHeld(::cs_main);
     713             : 
     714         389 :     auto bls_legacy_scheme = bls::bls_legacy_scheme.load();
     715             : 
     716             :     try {
     717         389 :         if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_V19) && !bls_legacy_scheme) {
     718             :             // NOTE: The block next to the activation is the one that is using new rules.
     719             :             // Removing the activation block here, so we must switch back to the old rules.
     720           0 :             bls::bls_legacy_scheme.store(true);
     721           0 :             LogPrintf("CSpecialTxProcessor::%s -- bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
     722           0 :         }
     723             : 
     724         389 :         if (!m_mnhfman.UndoBlock(block, pindex)) {
     725           0 :             return false;
     726             :         }
     727             : 
     728         389 :         if (!m_dmnman.UndoBlock(pindex, updatesRet)) {
     729           0 :             return false;
     730             :         }
     731             : 
     732         389 :         if (!m_qblockman.UndoBlock(block, pindex)) {
     733           0 :             return false;
     734             :         }
     735         389 :     } catch (const std::exception& e) {
     736           0 :         bls::bls_legacy_scheme.store(bls_legacy_scheme);
     737           0 :         LogPrintf("CSpecialTxProcessor::%s -- bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
     738           0 :         return error(strprintf("CSpecialTxProcessor::%s -- FAILURE! %s\n", __func__, e.what()).c_str());
     739           0 :     }
     740             : 
     741         389 :     return true;
     742         389 : }
     743             : 
     744       18236 : bool CSpecialTxProcessor::CheckCreditPoolDiffForBlock(const CBlock& block, const CBlockIndex* pindex, const CCbTx& cbTx,
     745             :                                                       BlockValidationState& state)
     746             : {
     747       18236 :     AssertLockHeld(::cs_main);
     748             : 
     749       18236 :     if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_DIP0008)) return true;
     750       18236 :     if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_V20)) return true;
     751             : 
     752             :     try {
     753       15204 :         const CAmount blockSubsidy = GetBlockSubsidy(pindex, m_consensus_params);
     754       30408 :         const auto creditPoolDiff = GetCreditPoolDiffForBlock(m_cpoolman, block,
     755       15204 :                                                               pindex->pprev, m_consensus_params, blockSubsidy, state);
     756       15204 :         if (!creditPoolDiff.has_value()) return false;
     757             : 
     758       15204 :         const CAmount target_balance{cbTx.creditPoolBalance};
     759             :         // But it maybe not included yet in previous block yet; in this case value must be 0
     760       15204 :         const CAmount locked_calculated{creditPoolDiff->GetTotalLocked()};
     761       15204 :         if (target_balance != locked_calculated) {
     762           0 :             LogPrintf("CSpecialTxProcessor::%s -- mismatched locked amount in CbTx: %lld against re-calculated: %lld\n", __func__, target_balance, locked_calculated);
     763           0 :             return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-assetlocked-amount");
     764             :         }
     765             : 
     766       15204 :     } catch (const std::exception& e) {
     767           0 :         LogPrintf("CSpecialTxProcessor::%s -- FAILURE! %s\n", __func__, e.what());
     768           0 :         return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-checkcreditpooldiff");
     769           0 :     }
     770             : 
     771       15204 :     return true;
     772       18236 : }
     773             : 
     774             : template <typename ProTx>
     775          63 : static bool CheckService(const ProTx& proTx, TxValidationState& state)
     776             : {
     777          63 :     switch (proTx.netInfo->Validate()) {
     778             :     case NetInfoStatus::BadAddress:
     779           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-addr");
     780             :     case NetInfoStatus::BadPort:
     781           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-port");
     782             :     case NetInfoStatus::BadType:
     783           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-addr-type");
     784             :     case NetInfoStatus::NotRoutable:
     785           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-addr-unroutable");
     786             :     case NetInfoStatus::Malformed:
     787           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
     788             :     case NetInfoStatus::Success:
     789          63 :         return true;
     790             :     // Shouldn't be possible during self-checks
     791             :     case NetInfoStatus::BadInput:
     792             :     case NetInfoStatus::Duplicate:
     793             :     case NetInfoStatus::MaxLimit:
     794           0 :         assert(false);
     795             :     } // no default case, so the compiler can warn about missing cases
     796           0 :     assert(false);
     797          63 : }
     798             : 
     799             : template <typename ProTx>
     800           0 : static bool CheckPlatformFields(const ProTx& proTx, bool is_extended_addr, TxValidationState& state)
     801             : {
     802           0 :     if (proTx.platformNodeID.IsNull()) {
     803           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-nodeid");
     804             :     }
     805             : 
     806           0 :     if (is_extended_addr) {
     807             :         // platformHTTPPort and platformP2PPort have been subsumed by netInfo. They should always be zero.
     808           0 :         if (proTx.platformP2PPort != 0) {
     809           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port");
     810             :         }
     811           0 :         if (proTx.platformHTTPPort != 0) {
     812           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port");
     813             :         }
     814           0 :         return true;
     815             :     }
     816             : 
     817           0 :     if (::IsNodeOnMainnet()) {
     818           0 :         if (proTx.platformP2PPort != ::MainParams().GetDefaultPlatformP2PPort()) {
     819           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port");
     820             :         }
     821           0 :         if (proTx.platformHTTPPort != ::MainParams().GetDefaultPlatformHTTPPort()) {
     822           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port");
     823             :         }
     824           0 :     }
     825           0 :     if (proTx.platformP2PPort == ::MainParams().GetDefaultPort()) {
     826           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port");
     827             :     }
     828           0 :     if (proTx.platformHTTPPort == ::MainParams().GetDefaultPort()) {
     829           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port");
     830             :     }
     831             : 
     832           0 :     const uint16_t core_port{proTx.netInfo->GetPrimary().GetPort()};
     833           0 :     if (proTx.platformP2PPort == proTx.platformHTTPPort || proTx.platformP2PPort == core_port ||
     834           0 :         proTx.platformHTTPPort == core_port) {
     835           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-dup-ports");
     836             :     }
     837             : 
     838           0 :     return true;
     839           0 : }
     840             : 
     841             : template <typename ProTx>
     842           7 : static bool CheckHashSig(const ProTx& proTx, const PKHash& pkhash, TxValidationState& state)
     843             : {
     844           9 :     if (std::string strError; !CHashSigner::VerifyHash(::SerializeHash(proTx), ToKeyID(pkhash), proTx.vchSig, strError)) {
     845           2 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig");
     846             :     }
     847           5 :     return true;
     848           7 : }
     849             : 
     850             : template <typename ProTx>
     851           2 : static bool CheckStringSig(const ProTx& proTx, const PKHash& pkhash, TxValidationState& state)
     852             : {
     853           4 :     if (std::string strError;
     854           2 :         !CMessageSigner::VerifyMessage(ToKeyID(pkhash), proTx.vchSig, proTx.MakeSignString(), strError)) {
     855           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig");
     856             :     }
     857           2 :     return true;
     858           2 : }
     859             : 
     860             : template <typename ProTx>
     861           8 : static bool CheckHashSig(const ProTx& proTx, const CBLSPublicKey& pubKey, TxValidationState& state)
     862             : {
     863           8 :     if (!proTx.sig.VerifyInsecure(pubKey, ::SerializeHash(proTx))) {
     864           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig");
     865             :     }
     866           8 :     return true;
     867           8 : }
     868             : 
     869             : template <typename ProTx>
     870          74 : static std::optional<ProTx> GetValidatedPayload(const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev,
     871             :                                                 const ChainstateManager& chainman, TxValidationState& state)
     872             : {
     873          74 :     if (tx.nType != ProTx::SPECIALTX_TYPE) {
     874           0 :         state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type");
     875           0 :         return std::nullopt;
     876             :     }
     877             : 
     878          74 :     auto opt_ptx = GetTxPayload<ProTx>(tx);
     879          74 :     if (!opt_ptx) {
     880           0 :         state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload");
     881           0 :         return std::nullopt;
     882             :     }
     883          74 :     if (!opt_ptx->IsTriviallyValid(pindexPrev, chainman, state)) {
     884             :         // pass the state returned by the function above
     885           0 :         return std::nullopt;
     886             :     }
     887          74 :     return opt_ptx;
     888          74 : }
     889             : 
     890             : /**
     891             :  * Validates potential changes to masternode state version by ProTx transaction version
     892             :  * @param[in]  pindexPrev    Previous block index to validate DEPLOYMENT_V24 activation
     893             :  * @param[in]  tx_type       Special transaction type
     894             :  * @param[in]  state_version Current masternode state version
     895             :  * @param[in]  tx_version    Proposed transaction version
     896             :  * @param[out] state         This may be set to an Error state if any error occurred processing them
     897             :  * @returns                  true if version change is valid or DEPLOYMENT_V24 is not active
     898             :  */
     899          15 : static bool IsVersionChangeValid(gsl::not_null<const CBlockIndex*> pindexPrev, const uint16_t tx_type,
     900             :                                  const uint16_t state_version, const uint16_t tx_version,
     901             :                                  const ChainstateManager& chainman, TxValidationState& state)
     902             : {
     903          15 :     if (!DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24)) {
     904             :         // New restrictions only apply after v24 deployment
     905          15 :         return true;
     906             :     }
     907             : 
     908           0 :     if (state_version >= ProTxVersion::BasicBLS && tx_version == ProTxVersion::LegacyBLS) {
     909             :         // Don't allow legacy scheme versioned transactions after upgrading to basic scheme
     910           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-downgrade");
     911             :     }
     912             : 
     913           0 :     if (state_version == ProTxVersion::LegacyBLS && tx_version > ProTxVersion::BasicBLS) {
     914             :         // Nodes using the legacy scheme must first upgrade to the basic scheme before upgrading further
     915           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-upgrade");
     916             :     }
     917             : 
     918           0 :     if (tx_type != TRANSACTION_PROVIDER_UPDATE_SERVICE && tx_version == ProTxVersion::ExtAddr) {
     919             :         // Only new entries (ProRegTx) and service updates (ProUpServTx) can use ExtAddr versioning
     920           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-tx-type");
     921             :     }
     922             : 
     923           0 :     return true;
     924          15 : }
     925             : 
     926          59 : bool CheckProRegTx(const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev,
     927             :                    CDeterministicMNManager& dmnman, const CCoinsViewCache& view, const ChainstateManager& chainman,
     928             :                    TxValidationState& state, bool check_sigs)
     929             : {
     930          59 :     const auto opt_ptx = GetValidatedPayload<CProRegTx>(tx, pindexPrev, chainman, state);
     931          59 :     if (!opt_ptx) {
     932             :         // pass the state returned by the function above
     933           0 :         return false;
     934             :     }
     935             : 
     936          59 :     const bool is_v24_active{DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24)};
     937             : 
     938             :     // No longer allow legacy scheme masternode registration
     939          59 :     if (is_v24_active && opt_ptx->nVersion < ProTxVersion::BasicBLS) {
     940           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-disallowed");
     941             :     }
     942             : 
     943             :     // It's allowed to set addr to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be
     944             :     // issues later. If any of both is set, it must be valid however
     945          59 :     if (!opt_ptx->netInfo->IsEmpty() && !CheckService(*opt_ptx, state)) {
     946             :         // pass the state returned by the function above
     947           0 :         return false;
     948             :     }
     949             : 
     950          59 :     if (opt_ptx->nType == MnType::Evo) {
     951           0 :         if (!CheckPlatformFields(*opt_ptx, opt_ptx->nVersion >= ProTxVersion::ExtAddr, state)) {
     952           0 :             return false;
     953             :         }
     954           0 :     }
     955             : 
     956          59 :     CTxDestination collateralTxDest;
     957          59 :     const PKHash* keyForPayloadSig = nullptr;
     958          59 :     COutPoint collateralOutpoint;
     959             : 
     960          59 :     CAmount expectedCollateral = GetMnType(opt_ptx->nType).collat_amount;
     961             : 
     962          59 :     if (!opt_ptx->collateralOutpoint.hash.IsNull()) {
     963           2 :         Coin coin;
     964           2 :         if (!view.GetCoin(opt_ptx->collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != expectedCollateral) {
     965           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral");
     966             :         }
     967             : 
     968           2 :         if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) {
     969           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-dest");
     970             :         }
     971             : 
     972             :         // Extract key from collateral. This only works for P2PK and P2PKH collaterals and will fail for P2SH.
     973             :         // Issuer of this ProRegTx must prove ownership with this key by signing the ProRegTx
     974           2 :         keyForPayloadSig = std::get_if<PKHash>(&collateralTxDest);
     975           2 :         if (!keyForPayloadSig) {
     976           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-pkh");
     977             :         }
     978             : 
     979           2 :         collateralOutpoint = opt_ptx->collateralOutpoint;
     980           2 :     } else {
     981          57 :         if (opt_ptx->collateralOutpoint.n >= tx.vout.size()) {
     982           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-index");
     983             :         }
     984          57 :         if (tx.vout[opt_ptx->collateralOutpoint.n].nValue != expectedCollateral) {
     985           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral");
     986             :         }
     987             : 
     988          57 :         if (!ExtractDestination(tx.vout[opt_ptx->collateralOutpoint.n].scriptPubKey, collateralTxDest)) {
     989           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-dest");
     990             :         }
     991             : 
     992          57 :         collateralOutpoint = COutPoint(tx.GetHash(), opt_ptx->collateralOutpoint.n);
     993             :     }
     994             : 
     995             :     // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
     996             :     // this check applies to internal and external collateral, but internal collaterals are not necessarily a P2PKH
     997         118 :     if (collateralTxDest == CTxDestination(PKHash(opt_ptx->keyIDOwner)) ||
     998          59 :         collateralTxDest == CTxDestination(PKHash(opt_ptx->keyIDVoting))) {
     999           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-reuse");
    1000             :     }
    1001             : 
    1002          59 :     if (pindexPrev) {
    1003          59 :         auto mnList = dmnman.GetListForBlock(pindexPrev);
    1004             : 
    1005             :         // only allow reusing of addresses when it's for the same collateral (which replaces the old MN)
    1006         118 :         for (const auto& entry : opt_ptx->netInfo->GetEntries()) {
    1007         118 :             if (const auto service_opt{entry.GetAddrPort()}) {
    1008          59 :                 if (mnList.HasUniqueProperty(*service_opt) &&
    1009           0 :                     mnList.GetUniquePropertyMN(*service_opt)->collateralOutpoint != collateralOutpoint) {
    1010           0 :                     return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
    1011             :                 }
    1012          59 :             } else if (const auto domain_opt{entry.GetDomainPort()}) {
    1013           0 :                 if (mnList.HasUniqueProperty(*domain_opt) &&
    1014           0 :                     mnList.GetUniquePropertyMN(*domain_opt)->collateralOutpoint != collateralOutpoint) {
    1015           0 :                     return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
    1016             :                 }
    1017           0 :             } else {
    1018           0 :                 return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-entry");
    1019             :             }
    1020             :         }
    1021             : 
    1022             :         // never allow duplicate keys, even if this ProTx would replace an existing MN
    1023          59 :         if (mnList.HasUniqueProperty(opt_ptx->keyIDOwner) || mnList.HasUniqueProperty(opt_ptx->pubKeyOperator)) {
    1024           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-key");
    1025             :         }
    1026             : 
    1027             :         // never allow duplicate platformNodeIds for EvoNodes
    1028          59 :         if (opt_ptx->nType == MnType::Evo) {
    1029           0 :             if (mnList.HasUniqueProperty(opt_ptx->platformNodeID)) {
    1030           0 :                 return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-platformnodeid");
    1031             :             }
    1032           0 :         }
    1033             : 
    1034          59 :         if (!DeploymentDIP0003Enforced(pindexPrev->nHeight, Params().GetConsensus())) {
    1035          25 :             if (opt_ptx->keyIDOwner != opt_ptx->keyIDVoting) {
    1036           0 :                 return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-not-same");
    1037             :             }
    1038          25 :         }
    1039          59 :     }
    1040             : 
    1041          59 :     if (!CheckInputsHash(tx, *opt_ptx, state)) {
    1042             :         // pass the state returned by the function above
    1043           0 :         return false;
    1044             :     }
    1045             : 
    1046          59 :     if (keyForPayloadSig) {
    1047             :         // collateral is not part of this ProRegTx, so we must verify ownership of the collateral
    1048           2 :         if (check_sigs && !CheckStringSig(*opt_ptx, *keyForPayloadSig, state)) {
    1049             :             // pass the state returned by the function above
    1050           0 :             return false;
    1051             :         }
    1052           2 :     } else {
    1053             :         // collateral is part of this ProRegTx, so we know the collateral is owned by the issuer
    1054          57 :         if (!opt_ptx->vchSig.empty()) {
    1055           0 :             return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig");
    1056             :         }
    1057             :     }
    1058             : 
    1059          59 :     return true;
    1060          59 : }
    1061             : 
    1062           4 : bool CheckProUpServTx(const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, CDeterministicMNManager& dmnman,
    1063             :                       const ChainstateManager& chainman, TxValidationState& state, bool check_sigs)
    1064             : {
    1065           4 :     const auto opt_ptx = GetValidatedPayload<CProUpServTx>(tx, pindexPrev, chainman, state);
    1066           4 :     if (!opt_ptx) {
    1067             :         // pass the state returned by the function above
    1068           0 :         return false;
    1069             :     }
    1070             : 
    1071           4 :     if (!CheckService(*opt_ptx, state)) {
    1072             :         // pass the state returned by the function above
    1073           0 :         return false;
    1074             :     }
    1075             : 
    1076           4 :     if (opt_ptx->nType == MnType::Evo) {
    1077           0 :         if (!CheckPlatformFields(*opt_ptx, opt_ptx->nVersion >= ProTxVersion::ExtAddr, state)) {
    1078           0 :             return false;
    1079             :         }
    1080           0 :     }
    1081             : 
    1082           4 :     auto mnList = dmnman.GetListForBlock(pindexPrev);
    1083           4 :     auto dmn = mnList.GetMN(opt_ptx->proTxHash);
    1084           4 :     if (!dmn) {
    1085           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash");
    1086             :     }
    1087             : 
    1088           4 :     if (!IsVersionChangeValid(pindexPrev, tx.nType, dmn->pdmnState->nVersion, opt_ptx->nVersion, chainman, state)) {
    1089             :         // pass the state returned by the function above
    1090           0 :         return false;
    1091             :     }
    1092             : 
    1093             :     // don't allow updating to addresses already used by other MNs
    1094           8 :     for (const auto& entry : opt_ptx->netInfo->GetEntries()) {
    1095           8 :         if (const auto service_opt{entry.GetAddrPort()}) {
    1096           4 :             if (mnList.HasUniqueProperty(*service_opt) &&
    1097           0 :                 mnList.GetUniquePropertyMN(*service_opt)->proTxHash != opt_ptx->proTxHash) {
    1098           0 :                 return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
    1099             :             }
    1100           4 :         } else if (const auto domain_opt{entry.GetDomainPort()}) {
    1101           0 :             if (mnList.HasUniqueProperty(*domain_opt) &&
    1102           0 :                 mnList.GetUniquePropertyMN(*domain_opt)->proTxHash != opt_ptx->proTxHash) {
    1103           0 :                 return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
    1104             :             }
    1105           0 :         } else {
    1106           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-entry");
    1107             :         }
    1108             :     }
    1109             : 
    1110             :     // don't allow updating to platformNodeIds already used by other EvoNodes
    1111           4 :     if (opt_ptx->nType == MnType::Evo) {
    1112           0 :         if (mnList.HasUniqueProperty(opt_ptx->platformNodeID) &&
    1113           0 :             mnList.GetUniquePropertyMN(opt_ptx->platformNodeID)->proTxHash != opt_ptx->proTxHash) {
    1114           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-platformnodeid");
    1115             :         }
    1116           0 :     }
    1117             : 
    1118           4 :     if (opt_ptx->scriptOperatorPayout != CScript()) {
    1119           0 :         if (dmn->nOperatorReward == 0) {
    1120             :             // don't allow setting operator reward payee in case no operatorReward was set
    1121           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-payee");
    1122             :         }
    1123           0 :         if (!opt_ptx->scriptOperatorPayout.IsPayToPublicKeyHash() && !opt_ptx->scriptOperatorPayout.IsPayToScriptHash()) {
    1124           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-payee");
    1125             :         }
    1126           0 :     }
    1127             : 
    1128             :     // we can only check the signature if pindexPrev != nullptr and the MN is known
    1129           4 :     if (!CheckInputsHash(tx, *opt_ptx, state)) {
    1130             :         // pass the state returned by the function above
    1131           0 :         return false;
    1132             :     }
    1133           4 :     if (check_sigs && !CheckHashSig(*opt_ptx, dmn->pdmnState->pubKeyOperator.Get(), state)) {
    1134             :         // pass the state returned by the function above
    1135           0 :         return false;
    1136             :     }
    1137             : 
    1138           4 :     return true;
    1139           4 : }
    1140             : 
    1141           7 : bool CheckProUpRegTx(const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev,
    1142             :                      CDeterministicMNManager& dmnman, const CCoinsViewCache& view, const ChainstateManager& chainman,
    1143             :                      TxValidationState& state, bool check_sigs)
    1144             : {
    1145           7 :     const auto opt_ptx = GetValidatedPayload<CProUpRegTx>(tx, pindexPrev, chainman, state);
    1146           7 :     if (!opt_ptx) {
    1147             :         // pass the state returned by the function above
    1148           0 :         return false;
    1149             :     }
    1150             : 
    1151           7 :     CTxDestination payoutDest;
    1152           7 :     if (!ExtractDestination(opt_ptx->scriptPayout, payoutDest)) {
    1153             :         // should not happen as we checked script types before
    1154           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-dest");
    1155             :     }
    1156             : 
    1157           7 :     auto mnList = dmnman.GetListForBlock(pindexPrev);
    1158           7 :     auto dmn = mnList.GetMN(opt_ptx->proTxHash);
    1159           7 :     if (!dmn) {
    1160           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash");
    1161             :     }
    1162             : 
    1163           7 :     if (!IsVersionChangeValid(pindexPrev, tx.nType, dmn->pdmnState->nVersion, opt_ptx->nVersion, chainman, state)) {
    1164             :         // pass the state returned by the function above
    1165           0 :         return false;
    1166             :     }
    1167             : 
    1168             :     // don't allow reuse of payee key for other keys (don't allow people to put the payee key onto an online server)
    1169          14 :     if (payoutDest == CTxDestination(PKHash(dmn->pdmnState->keyIDOwner)) ||
    1170           7 :         payoutDest == CTxDestination(PKHash(opt_ptx->keyIDVoting))) {
    1171           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-reuse");
    1172             :     }
    1173             : 
    1174           7 :     Coin coin;
    1175           7 :     if (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent()) {
    1176             :         // this should never happen (there would be no dmn otherwise)
    1177           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-collateral");
    1178             :     }
    1179             : 
    1180             :     // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
    1181           7 :     CTxDestination collateralTxDest;
    1182           7 :     if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) {
    1183           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-collateral-dest");
    1184             :     }
    1185          14 :     if (collateralTxDest == CTxDestination(PKHash(dmn->pdmnState->keyIDOwner)) ||
    1186           7 :         collateralTxDest == CTxDestination(PKHash(opt_ptx->keyIDVoting))) {
    1187           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-reuse");
    1188             :     }
    1189             : 
    1190           7 :     if (mnList.HasUniqueProperty(opt_ptx->pubKeyOperator)) {
    1191           0 :         auto otherDmn = mnList.GetUniquePropertyMN(opt_ptx->pubKeyOperator);
    1192           0 :         if (opt_ptx->proTxHash != otherDmn->proTxHash) {
    1193           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-key");
    1194             :         }
    1195           0 :     }
    1196             : 
    1197           7 :     if (!DeploymentDIP0003Enforced(pindexPrev->nHeight, Params().GetConsensus())) {
    1198           1 :         if (dmn->pdmnState->keyIDOwner != opt_ptx->keyIDVoting) {
    1199           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-not-same");
    1200             :         }
    1201           1 :     }
    1202             : 
    1203           7 :     if (!CheckInputsHash(tx, *opt_ptx, state)) {
    1204             :         // pass the state returned by the function above
    1205           0 :         return false;
    1206             :     }
    1207           7 :     if (check_sigs && !CheckHashSig(*opt_ptx, PKHash(dmn->pdmnState->keyIDOwner), state)) {
    1208             :         // pass the state returned by the function above
    1209           2 :         return false;
    1210             :     }
    1211             : 
    1212           5 :     return true;
    1213           7 : }
    1214             : 
    1215           4 : bool CheckProUpRevTx(const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, CDeterministicMNManager& dmnman,
    1216             :                      const ChainstateManager& chainman, TxValidationState& state, bool check_sigs)
    1217             : {
    1218           4 :     const auto opt_ptx = GetValidatedPayload<CProUpRevTx>(tx, pindexPrev, chainman, state);
    1219           4 :     if (!opt_ptx) {
    1220             :         // pass the state returned by the function above
    1221           0 :         return false;
    1222             :     }
    1223             : 
    1224           4 :     auto mnList = dmnman.GetListForBlock(pindexPrev);
    1225           4 :     auto dmn = mnList.GetMN(opt_ptx->proTxHash);
    1226           4 :     if (!dmn) {
    1227           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash");
    1228             :     }
    1229             : 
    1230           4 :     if (!IsVersionChangeValid(pindexPrev, tx.nType, dmn->pdmnState->nVersion, opt_ptx->nVersion, chainman, state)) {
    1231             :         // pass the state returned by the function above
    1232           0 :         return false;
    1233             :     }
    1234             : 
    1235           4 :     if (!CheckInputsHash(tx, *opt_ptx, state)) {
    1236             :         // pass the state returned by the function above
    1237           0 :         return false;
    1238             :     }
    1239           4 :     if (check_sigs && !CheckHashSig(*opt_ptx, dmn->pdmnState->pubKeyOperator.Get(), state)) {
    1240             :         // pass the state returned by the function above
    1241           0 :         return false;
    1242             :     }
    1243             : 
    1244           4 :     return true;
    1245           4 : }
    1246             : 
    1247          95 : bool IsStandardSpecialTx(const CTransaction& tx, std::string& reason)
    1248             : {
    1249          95 :     if (!tx.IsSpecialTxVersion()) return true;
    1250             : 
    1251           0 :     if (tx.nType != TRANSACTION_ASSET_LOCK) return true;
    1252             : 
    1253             :     // Each input is referenced by Platform's funding state transition; beyond this
    1254             :     // many inputs that state transition exceeds Platform's ~20 kB size limit.
    1255             :     static constexpr size_t MAX_STANDARD_ASSET_LOCK_INPUTS{100};
    1256           0 :     if (tx.vin.size() > MAX_STANDARD_ASSET_LOCK_INPUTS) {
    1257           0 :         reason = "assetlocktx-too-many-inputs";
    1258           0 :         return false;
    1259             :     }
    1260             : 
    1261           0 :     constexpr int max_tx_size_for_platform = 20480;
    1262           0 :     if (tx.GetTotalSize() > max_tx_size_for_platform) {
    1263           0 :         reason = "assetlocktx-too-big";
    1264           0 :         return false;
    1265             :     }
    1266             : 
    1267           0 :     if (const auto opt_assetLockTx = GetTxPayload<CAssetLockPayload>(tx);
    1268           0 :         opt_assetLockTx.has_value() && opt_assetLockTx->getVersion() >= 2) {
    1269           0 :         reason = "assetlocktx-version-2";
    1270           0 :         return false;
    1271             :     }
    1272             : 
    1273           0 :     return true;
    1274          95 : }

Generated by: LCOV version 1.16