LCOV - code coverage report
Current view: top level - src/evo - providertx.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 115 143 80.4 %
Date: 2026-06-25 07:23:51 Functions: 15 19 78.9 %

          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/providertx.h>
       6             : 
       7             : #include <evo/dmn_types.h>
       8             : #include <util/std23.h>
       9             : 
      10             : #include <chainparams.h>
      11             : #include <consensus/validation.h>
      12             : #include <deploymentstatus.h>
      13             : #include <hash.h>
      14             : #include <script/standard.h>
      15             : #include <tinyformat.h>
      16             : #include <validation.h>
      17             : 
      18             : namespace ProTxVersion {
      19             : template <typename T>
      20         146 : [[nodiscard]] uint16_t GetMaxFromDeployment(gsl::not_null<const CBlockIndex*> pindexPrev,
      21             :                                             const ChainstateManager& chainman, std::optional<bool> is_basic_override)
      22             : {
      23         146 :     constexpr bool is_extaddr_eligible{std::is_same_v<std::decay_t<T>, CProRegTx> || std::is_same_v<std::decay_t<T>, CProUpServTx>};
      24         146 :     return ProTxVersion::GetMax(
      25         146 :         is_basic_override ? *is_basic_override
      26         146 :                           : DeploymentActiveAfter(pindexPrev, chainman.GetConsensus(), Consensus::DEPLOYMENT_V19),
      27         103 :         is_extaddr_eligible ? DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24) : false);
      28             : }
      29             : template uint16_t GetMaxFromDeployment<CProRegTx>(gsl::not_null<const CBlockIndex*> pindexPrev,
      30             :                                                   const ChainstateManager& chainman,
      31             :                                                   std::optional<bool> is_basic_override);
      32             : template uint16_t GetMaxFromDeployment<CProUpServTx>(gsl::not_null<const CBlockIndex*> pindexPrev,
      33             :                                                      const ChainstateManager& chainman,
      34             :                                                      std::optional<bool> is_basic_override);
      35             : template uint16_t GetMaxFromDeployment<CProUpRegTx>(gsl::not_null<const CBlockIndex*> pindexPrev,
      36             :                                                     const ChainstateManager& chainman,
      37             :                                                     std::optional<bool> is_basic_override);
      38             : template uint16_t GetMaxFromDeployment<CProUpRevTx>(gsl::not_null<const CBlockIndex*> pindexPrev,
      39             :                                                     const ChainstateManager& chainman,
      40             :                                                     std::optional<bool> is_basic_override);
      41             : } // namespace ProTxVersion
      42             : 
      43             : template <typename ProTx>
      44          93 : bool IsNetInfoTriviallyValid(const ProTx& proTx, TxValidationState& state)
      45             : {
      46          93 :     if (!proTx.netInfo->HasEntries(NetInfoPurpose::CORE_P2P)) {
      47             :         // Mandatory for all nodes
      48           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
      49             :     }
      50          93 :     if (proTx.nType == MnType::Regular) {
      51             :         // Regular nodes shouldn't populate Platform-specific fields
      52          91 :         if (proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) ||
      53          91 :             proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_P2P)) {
      54           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
      55             :         }
      56          91 :     }
      57          93 :     if (proTx.netInfo->CanStorePlatform() && proTx.nType == MnType::Evo) {
      58             :         // Platform fields are mandatory for EvoNodes
      59           0 :         if (!proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) ||
      60           0 :             !proTx.netInfo->HasEntries(NetInfoPurpose::PLATFORM_P2P)) {
      61           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
      62             :         }
      63           0 :     }
      64          93 :     return true;
      65          93 : }
      66             : 
      67          85 : bool CProRegTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
      68             :                                  TxValidationState& state) const
      69             : {
      70          85 :     if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
      71           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
      72             :     }
      73          85 :     if (nVersion < ProTxVersion::BasicBLS && nType == MnType::Evo) {
      74           4 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-evo-version");
      75             :     }
      76          81 :     if (!IsValidMnType(nType)) {
      77           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type");
      78             :     }
      79          81 :     if (nMode != 0) {
      80           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-mode");
      81             :     }
      82             : 
      83          81 :     if (keyIDOwner.IsNull() || !pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) {
      84           2 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-null");
      85             :     }
      86          79 :     if (pubKeyOperator.IsLegacy() != (nVersion == ProTxVersion::LegacyBLS)) {
      87           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-pubkey");
      88             :     }
      89          79 :     if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
      90           2 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee");
      91             :     }
      92          77 :     if (netInfo->CanStorePlatform() != (nVersion == ProTxVersion::ExtAddr)) {
      93           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-netinfo-version");
      94             :     }
      95          77 :     if (!netInfo->IsEmpty() && !IsNetInfoTriviallyValid(*this, state)) {
      96             :         // pass the state returned by the function above
      97           0 :         return false;
      98             :     }
      99         154 :     for (const auto& entry : netInfo->GetEntries()) {
     100          77 :         if (!entry.IsTriviallyValid()) {
     101           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
     102             :         }
     103             :     }
     104             : 
     105          77 :     CTxDestination payoutDest;
     106          77 :     if (!ExtractDestination(scriptPayout, payoutDest)) {
     107             :         // should not happen as we checked script types before
     108           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-dest");
     109             :     }
     110             :     // don't allow reuse of payout key for other keys (don't allow people to put the payee key onto an online server)
     111          77 :     if (payoutDest == CTxDestination(PKHash(keyIDOwner)) || payoutDest == CTxDestination(PKHash(keyIDVoting))) {
     112           2 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-reuse");
     113             :     }
     114             : 
     115          75 :     if (nOperatorReward > 10000) {
     116           2 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-reward");
     117             :     }
     118             : 
     119          73 :     return true;
     120          85 : }
     121             : 
     122           7 : std::string CProRegTx::MakeSignString() const
     123             : {
     124           7 :     std::string s;
     125             : 
     126             :     // We only include the important stuff in the string form...
     127             : 
     128           7 :     CTxDestination destPayout;
     129           7 :     std::string strPayout;
     130           7 :     if (ExtractDestination(scriptPayout, destPayout)) {
     131           7 :         strPayout = EncodeDestination(destPayout);
     132           7 :     } else {
     133           0 :         strPayout = HexStr(scriptPayout);
     134             :     }
     135             : 
     136           7 :     s += strPayout + "|";
     137           7 :     s += strprintf("%d", nOperatorReward) + "|";
     138           7 :     s += EncodeDestination(PKHash(keyIDOwner)) + "|";
     139           7 :     s += EncodeDestination(PKHash(keyIDVoting)) + "|";
     140             : 
     141             :     // ... and also the full hash of the payload as a protection against malleability and replays
     142           7 :     s += ::SerializeHash(*this).ToString();
     143             : 
     144           7 :     return s;
     145           7 : }
     146             : 
     147          69 : std::string CProRegTx::ToString() const
     148             : {
     149          69 :     CTxDestination dest;
     150          69 :     std::string payee = "unknown";
     151          69 :     if (ExtractDestination(scriptPayout, dest)) {
     152          69 :         payee = EncodeDestination(dest);
     153          69 :     }
     154             : 
     155          69 :     return strprintf("CProRegTx(nVersion=%d, nType=%d, collateralOutpoint=%s, netInfo=%s, nOperatorReward=%f, "
     156             :                      "ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, scriptPayout=%s, platformNodeID=%s%s)\n",
     157          69 :                      nVersion, std23::to_underlying(nType), collateralOutpoint.ToStringShort(), netInfo->ToString(),
     158          69 :                      (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(),
     159          69 :                      EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(),
     160         138 :                      (nVersion >= ProTxVersion::ExtAddr
     161           0 :                           ? ""
     162          69 :                           : strprintf(", platformP2PPort=%d, platformHTTPPort=%d", platformP2PPort, platformHTTPPort)));
     163          69 : }
     164             : 
     165          18 : bool CProUpServTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
     166             :                                     TxValidationState& state) const
     167             : {
     168          18 :     if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
     169           2 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
     170             :     }
     171          16 :     if (nVersion < ProTxVersion::BasicBLS && nType == MnType::Evo) {
     172           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-evo-version");
     173             :     }
     174          16 :     if (netInfo->CanStorePlatform() != (nVersion == ProTxVersion::ExtAddr)) {
     175           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-netinfo-version");
     176             :     }
     177          16 :     if (netInfo->IsEmpty()) {
     178           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-empty");
     179             :     }
     180          16 :     if (!IsNetInfoTriviallyValid(*this, state)) {
     181             :         // pass the state returned by the function above
     182           0 :         return false;
     183             :     }
     184          32 :     for (const auto& entry : netInfo->GetEntries()) {
     185          16 :         if (!entry.IsTriviallyValid()) {
     186           0 :             return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
     187             :         }
     188             :     }
     189             : 
     190          16 :     return true;
     191          18 : }
     192             : 
     193           8 : std::string CProUpServTx::ToString() const
     194             : {
     195           8 :     CTxDestination dest;
     196           8 :     std::string payee = "unknown";
     197           8 :     if (ExtractDestination(scriptOperatorPayout, dest)) {
     198           0 :         payee = EncodeDestination(dest);
     199           0 :     }
     200             : 
     201           8 :     return strprintf("CProUpServTx(nVersion=%d, nType=%d, proTxHash=%s, netInfo=%s, operatorPayoutAddress=%s, "
     202             :                      "platformNodeID=%s%s)\n",
     203           8 :                      nVersion, std23::to_underlying(nType), proTxHash.ToString(), netInfo->ToString(), payee,
     204           8 :                      platformNodeID.ToString(),
     205          16 :                      (nVersion >= ProTxVersion::ExtAddr
     206           0 :                           ? ""
     207           8 :                           : strprintf(", platformP2PPort=%d, platformHTTPPort=%d", platformP2PPort, platformHTTPPort)));
     208           8 : }
     209             : 
     210          23 : bool CProUpRegTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
     211             :                                    TxValidationState& state) const
     212             : {
     213          23 :     if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
     214           2 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
     215             :     }
     216          21 :     if (nMode != 0) {
     217           0 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-mode");
     218             :     }
     219             : 
     220          21 :     if (!pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) {
     221           2 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-null");
     222             :     }
     223          19 :     if (pubKeyOperator.IsLegacy() != (nVersion == ProTxVersion::LegacyBLS)) {
     224           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-pubkey");
     225             :     }
     226          19 :     if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
     227           0 :         return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee");
     228             :     }
     229          19 :     return true;
     230          23 : }
     231             : 
     232           6 : std::string CProUpRegTx::ToString() const
     233             : {
     234           6 :     CTxDestination dest;
     235           6 :     std::string payee = "unknown";
     236           6 :     if (ExtractDestination(scriptPayout, dest)) {
     237           6 :         payee = EncodeDestination(dest);
     238           6 :     }
     239             : 
     240           6 :     return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, pubKeyOperator=%s, votingAddress=%s, payoutAddress=%s)",
     241           6 :         nVersion, proTxHash.ToString(), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), payee);
     242           6 : }
     243             : 
     244          20 : bool CProUpRevTx::IsTriviallyValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ChainstateManager& chainman,
     245             :                                    TxValidationState& state) const
     246             : {
     247          20 :     if (nVersion == 0 || nVersion > ProTxVersion::GetMaxFromDeployment<decltype(*this)>(pindexPrev, chainman)) {
     248           2 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version");
     249             :     }
     250             : 
     251             :     // nReason < CProUpRevTx::REASON_NOT_SPECIFIED is always `false` since
     252             :     // nReason is unsigned and CProUpRevTx::REASON_NOT_SPECIFIED == 0
     253          18 :     if (nReason > CProUpRevTx::REASON_LAST) {
     254           2 :         return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-reason");
     255             :     }
     256          16 :     return true;
     257          20 : }
     258             : 
     259           7 : std::string CProUpRevTx::ToString() const
     260             : {
     261           7 :     return strprintf("CProUpRevTx(nVersion=%d, proTxHash=%s, nReason=%d)",
     262           7 :         nVersion, proTxHash.ToString(), nReason);
     263           0 : }

Generated by: LCOV version 1.16