LCOV - code coverage report
Current view: top level - src/evo - specialtx_filter.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 47 47 100.0 %
Date: 2026-06-25 07:23:43 Functions: 4 4 100.0 %

          Line data    Source code
       1             : // Copyright (c) 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/specialtx_filter.h>
       6             : 
       7             : #include <evo/assetlocktx.h>
       8             : #include <evo/providertx.h>
       9             : #include <evo/specialtx.h>
      10             : #include <primitives/transaction.h>
      11             : #include <script/script.h>
      12             : #include <span.h>
      13             : #include <streams.h>
      14             : 
      15             : /**
      16             :  * Rationale for Special Transaction Field Extraction:
      17             :  *
      18             :  * This implementation extracts specific fields from Dash special transactions
      19             :  * to maintain parity with the bloom filter implementation (CBloomFilter::CheckSpecialTransactionMatchesAndUpdate).
      20             :  *
      21             :  * The fields extracted are those that SPV clients might need to detect:
      22             :  * - Owner/Voting keys: To track masternode ownership and voting rights
      23             :  * - Payout scripts: To detect payments to specific addresses
      24             :  * - ProTx hashes: To track masternode lifecycle and updates
      25             :  * - Collateral outpoints: To track masternode collateral
      26             :  * - Credit outputs: To track platform-related transactions
      27             :  *
      28             :  * Each transaction type has different fields based on its purpose:
      29             :  * - ProRegTx: All identity and payout fields (initial registration)
      30             :  * - ProUpServTx: ProTx hash and operator payout (service updates)
      31             :  * - ProUpRegTx: ProTx hash, voting key, and payout (ownership updates)
      32             :  * - ProUpRevTx: ProTx hash only (revocation tracking)
      33             :  * - AssetLockTx: Credit output scripts (platform credits)
      34             :  */
      35             : // Helper function to add a script to the filter if it's not empty
      36        7876 : static void AddScriptElement(const CScript& script, const std::function<void(Span<const unsigned char>)>& addElement)
      37             : {
      38        7876 :     if (!script.empty()) {
      39        6680 :         addElement(MakeUCharSpan(script));
      40        6680 :     }
      41        7876 : }
      42             : 
      43             : // Helper function to add a hash/key to the filter
      44             : template <typename T>
      45       10156 : static void AddHashElement(const T& hash, const std::function<void(Span<const unsigned char>)>& addElement)
      46             : {
      47       10156 :     addElement(MakeUCharSpan(hash));
      48       10156 : }
      49             : 
      50             : // NOTE(maintenance): Keep this in sync with
      51             : // CBloomFilter::CheckSpecialTransactionMatchesAndUpdate in
      52             : // src/common/bloom.cpp. If you add or remove fields for a special
      53             : // transaction type here, update the bloom filter routine accordingly
      54             : // (and vice versa) to avoid compact-filter vs bloom-filter divergence.
      55      345001 : void ExtractSpecialTxFilterElements(const CTransaction& tx, const std::function<void(Span<const unsigned char>)>& addElement)
      56             : {
      57      345001 :     if (!tx.HasExtraPayloadField()) {
      58       19458 :         return; // not a special transaction
      59             :     }
      60             : 
      61      325543 :     switch (tx.nType) {
      62             :     case TRANSACTION_PROVIDER_REGISTER: {
      63        6812 :         if (const auto opt_proTx = GetTxPayload<CProRegTx>(tx)) {
      64             :             // Add collateral outpoint
      65        3406 :             CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
      66        3406 :             stream << opt_proTx->collateralOutpoint;
      67        3406 :             addElement(MakeUCharSpan(stream));
      68             : 
      69             :             // Add owner key ID
      70        3406 :             AddHashElement(opt_proTx->keyIDOwner, addElement);
      71             : 
      72             :             // Add voting key ID
      73        3406 :             AddHashElement(opt_proTx->keyIDVoting, addElement);
      74             : 
      75             :             // Add payout script
      76        3406 :             AddScriptElement(opt_proTx->scriptPayout, addElement);
      77        3406 :         }
      78        3406 :         break;
      79             :     }
      80             :     case TRANSACTION_PROVIDER_UPDATE_SERVICE: {
      81        6130 :         if (const auto opt_proTx = GetTxPayload<CProUpServTx>(tx)) {
      82             :             // Add ProTx hash
      83        3065 :             AddHashElement(opt_proTx->proTxHash, addElement);
      84             : 
      85             :             // Add operator payout script
      86        3065 :             AddScriptElement(opt_proTx->scriptOperatorPayout, addElement);
      87        3065 :         }
      88        3065 :         break;
      89             :     }
      90             :     case TRANSACTION_PROVIDER_UPDATE_REGISTRAR: {
      91         194 :         if (const auto opt_proTx = GetTxPayload<CProUpRegTx>(tx)) {
      92             :             // Add ProTx hash
      93          97 :             AddHashElement(opt_proTx->proTxHash, addElement);
      94             : 
      95             :             // Add voting key ID
      96          97 :             AddHashElement(opt_proTx->keyIDVoting, addElement);
      97             : 
      98             :             // Add payout script
      99          97 :             AddScriptElement(opt_proTx->scriptPayout, addElement);
     100          97 :         }
     101          97 :         break;
     102             :     }
     103             :     case TRANSACTION_PROVIDER_UPDATE_REVOKE: {
     104         170 :         if (const auto opt_proTx = GetTxPayload<CProUpRevTx>(tx)) {
     105             :             // Add ProTx hash
     106          85 :             AddHashElement(opt_proTx->proTxHash, addElement);
     107          85 :         }
     108          85 :         break;
     109             :     }
     110             :     case TRANSACTION_ASSET_LOCK: {
     111             :         // Asset Lock transactions have special outputs (creditOutputs) that should be included
     112        1336 :         if (const auto opt_assetlockTx = GetTxPayload<CAssetLockPayload>(tx)) {
     113         668 :             const auto& extraOuts = opt_assetlockTx->getCreditOutputs();
     114        1977 :             for (const CTxOut& txout : extraOuts) {
     115        1309 :                 const CScript& script = txout.scriptPubKey;
     116             :                 // Exclude OP_RETURN outputs as they are not spendable
     117        1309 :                 if (!script.empty() && script[0] != OP_RETURN) {
     118        1308 :                     AddScriptElement(script, addElement);
     119        1308 :                 }
     120             :             }
     121         668 :         }
     122         668 :         break;
     123             :     }
     124             :     case TRANSACTION_ASSET_UNLOCK:
     125             :     case TRANSACTION_COINBASE:
     126             :     case TRANSACTION_QUORUM_COMMITMENT:
     127             :     case TRANSACTION_MNHF_SIGNAL:
     128             :         // No additional special fields needed for these transaction types
     129             :         // Their standard outputs are already included in the base filter
     130      318222 :         break;
     131             :     } // no default case, so the compiler can warn about missing cases
     132      345001 : }

Generated by: LCOV version 1.16