LCOV - code coverage report
Current view: top level - src - blockfilter.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 136 152 89.5 %
Date: 2026-06-25 07:23:51 Functions: 25 27 92.6 %

          Line data    Source code
       1             : // Copyright (c) 2018-2021 The Bitcoin 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 <mutex>
       6             : #include <set>
       7             : 
       8             : #include <blockfilter.h>
       9             : #include <crypto/siphash.h>
      10             : #include <evo/specialtx_filter.h>
      11             : #include <hash.h>
      12             : #include <primitives/transaction.h>
      13             : #include <script/script.h>
      14             : #include <streams.h>
      15             : #include <util/golombrice.h>
      16             : #include <util/string.h>
      17             : 
      18             : /// SerType used to serialize parameters in GCS filter encoding.
      19             : static constexpr int GCS_SER_TYPE = SER_NETWORK;
      20             : 
      21             : /// Protocol version used to serialize parameters in GCS filter encoding.
      22             : static constexpr int GCS_SER_VERSION = 0;
      23             : 
      24         146 : static const std::map<BlockFilterType, std::string> g_filter_types = {
      25         146 :     {BlockFilterType::BASIC_FILTER, "basic"},
      26             : };
      27             : 
      28       10588 : uint64_t GCSFilter::HashToRange(const Element& element) const
      29             : {
      30       21176 :     uint64_t hash = CSipHasher(m_params.m_siphash_k0, m_params.m_siphash_k1)
      31       10588 :         .Write(element.data(), element.size())
      32       10588 :         .Finalize();
      33       10588 :     return FastRange64(hash, m_F);
      34             : }
      35             : 
      36         335 : std::vector<uint64_t> GCSFilter::BuildHashedSet(const ElementSet& elements) const
      37             : {
      38         335 :     std::vector<uint64_t> hashed_elements;
      39         335 :     hashed_elements.reserve(elements.size());
      40       10798 :     for (const Element& element : elements) {
      41       10463 :         hashed_elements.push_back(HashToRange(element));
      42             :     }
      43         335 :     std::sort(hashed_elements.begin(), hashed_elements.end());
      44         335 :     return hashed_elements;
      45         335 : }
      46             : 
      47        2038 : GCSFilter::GCSFilter(const Params& params)
      48        1019 :     : m_params(params), m_N(0), m_F(0), m_encoded{0}
      49        2038 : {}
      50             : 
      51         668 : GCSFilter::GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter, bool skip_decode_check)
      52         334 :     : m_params(params), m_encoded(std::move(encoded_filter))
      53         334 : {
      54         334 :     SpanReader stream{GCS_SER_TYPE, GCS_SER_VERSION, m_encoded};
      55             : 
      56         334 :     uint64_t N = ReadCompactSize(stream);
      57         334 :     m_N = static_cast<uint32_t>(N);
      58         334 :     if (m_N != N) {
      59           0 :         throw std::ios_base::failure("N must be <2^32");
      60             :     }
      61         334 :     m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M);
      62             : 
      63         334 :     if (skip_decode_check) return;
      64             : 
      65             :     // Verify that the encoded filter contains exactly N elements. If it has too much or too little
      66             :     // data, a std::ios_base::failure exception will be raised.
      67           1 :     BitStreamReader<SpanReader> bitreader{stream};
      68           6 :     for (uint64_t i = 0; i < m_N; ++i) {
      69           5 :         GolombRiceDecode(bitreader, m_params.m_P);
      70           5 :     }
      71           1 :     if (!stream.empty()) {
      72           0 :         throw std::ios_base::failure("encoded_filter contains excess data");
      73             :     }
      74         668 : }
      75             : 
      76         705 : GCSFilter::GCSFilter(const Params& params, const ElementSet& elements)
      77         235 :     : m_params(params)
      78         235 : {
      79         235 :     size_t N = elements.size();
      80         235 :     m_N = static_cast<uint32_t>(N);
      81         235 :     if (m_N != N) {
      82           0 :         throw std::invalid_argument("N must be <2^32");
      83             :     }
      84         235 :     m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M);
      85             : 
      86         235 :     CVectorWriter stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
      87             : 
      88         235 :     WriteCompactSize(stream, m_N);
      89             : 
      90         235 :     if (elements.empty()) {
      91           0 :         return;
      92             :     }
      93             : 
      94         235 :     BitStreamWriter<CVectorWriter> bitwriter(stream);
      95             : 
      96         235 :     uint64_t last_value = 0;
      97         616 :     for (uint64_t value : BuildHashedSet(elements)) {
      98         381 :         uint64_t delta = value - last_value;
      99         381 :         GolombRiceEncode(bitwriter, m_params.m_P, delta);
     100         381 :         last_value = value;
     101             :     }
     102             : 
     103         235 :     bitwriter.Flush();
     104         470 : }
     105             : 
     106         225 : bool GCSFilter::MatchInternal(const uint64_t* element_hashes, size_t size) const
     107             : {
     108         225 :     SpanReader stream{GCS_SER_TYPE, GCS_SER_VERSION, m_encoded};
     109             : 
     110             :     // Seek forward by size of N
     111         225 :     uint64_t N = ReadCompactSize(stream);
     112         225 :     assert(N == m_N);
     113             : 
     114         225 :     BitStreamReader<SpanReader> bitreader{stream};
     115             : 
     116         225 :     uint64_t value = 0;
     117         225 :     size_t hashes_index = 0;
     118        9168 :     for (uint32_t i = 0; i < m_N; ++i) {
     119        9164 :         uint64_t delta = GolombRiceDecode(bitreader, m_params.m_P);
     120        9164 :         value += delta;
     121             : 
     122       13043 :         while (true) {
     123       13043 :             if (hashes_index == size) {
     124           2 :                 return false;
     125       13041 :             } else if (element_hashes[hashes_index] == value) {
     126         219 :                 return true;
     127       12822 :             } else if (element_hashes[hashes_index] > value) {
     128        8943 :                 break;
     129             :             }
     130             : 
     131        3879 :             hashes_index++;
     132             :         }
     133        8943 :     }
     134             : 
     135           4 :     return false;
     136         225 : }
     137             : 
     138         125 : bool GCSFilter::Match(const Element& element) const
     139             : {
     140         125 :     uint64_t query = HashToRange(element);
     141         125 :     return MatchInternal(&query, 1);
     142             : }
     143             : 
     144         100 : bool GCSFilter::MatchAny(const ElementSet& elements) const
     145             : {
     146         100 :     const std::vector<uint64_t> queries = BuildHashedSet(elements);
     147         100 :     return MatchInternal(queries.data(), queries.size());
     148         100 : }
     149             : 
     150          98 : const std::string& BlockFilterTypeName(BlockFilterType filter_type)
     151             : {
     152          98 :     static std::string unknown_retval;
     153          98 :     auto it = g_filter_types.find(filter_type);
     154          98 :     return it != g_filter_types.end() ? it->second : unknown_retval;
     155             : }
     156             : 
     157           2 : bool BlockFilterTypeByName(const std::string& name, BlockFilterType& filter_type) {
     158           3 :     for (const auto& entry : g_filter_types) {
     159           2 :         if (entry.second == name) {
     160           1 :             filter_type = entry.first;
     161           1 :             return true;
     162             :         }
     163             :     }
     164           1 :     return false;
     165           2 : }
     166             : 
     167           0 : const std::set<BlockFilterType>& AllBlockFilterTypes()
     168             : {
     169           0 :     static std::set<BlockFilterType> types;
     170             : 
     171             :     static std::once_flag flag;
     172           0 :     std::call_once(flag, []() {
     173           0 :             for (const auto& entry : g_filter_types) {
     174           0 :                 types.insert(entry.first);
     175             :             }
     176           0 :         });
     177             : 
     178           0 :     return types;
     179             : }
     180             : 
     181         627 : const std::string& ListBlockFilterTypes()
     182             : {
     183         743 :     static std::string type_list{Join(g_filter_types, ", ", [](const auto& entry) { return entry.second; })};
     184             : 
     185         627 :     return type_list;
     186           0 : }
     187             : 
     188         234 : static GCSFilter::ElementSet BasicFilterElements(const CBlock& block,
     189             :                                                  const CBlockUndo& block_undo)
     190             : {
     191         234 :     GCSFilter::ElementSet elements;
     192             : 
     193         484 :     for (const CTransactionRef& tx : block.vtx) {
     194         505 :         for (const CTxOut& txout : tx->vout) {
     195         255 :             const CScript& script = txout.scriptPubKey;
     196         255 :             if (script.empty() || script[0] == OP_RETURN) continue;
     197         250 :             elements.emplace(script.begin(), script.end());
     198             :         }
     199             : 
     200             :         // Extract special transaction elements using delegation pattern
     201         266 :         ExtractSpecialTxFilterElements(*tx, [&elements](Span<const unsigned char> data) {
     202          16 :             elements.emplace(data.begin(), data.end());
     203          16 :         });
     204             :     }
     205             : 
     206         250 :     for (const CTxUndo& tx_undo : block_undo.vtxundo) {
     207          43 :         for (const Coin& prevout : tx_undo.vprevout) {
     208          27 :             const CScript& script = prevout.out.scriptPubKey;
     209          27 :             if (script.empty()) continue;
     210          23 :             elements.emplace(script.begin(), script.end());
     211             :         }
     212             :     }
     213             : 
     214         234 :     return elements;
     215         234 : }
     216             : 
     217         999 : BlockFilter::BlockFilter(BlockFilterType filter_type, const uint256& block_hash,
     218             :                          std::vector<unsigned char> filter, bool skip_decode_check)
     219         333 :     : m_filter_type(filter_type), m_block_hash(block_hash)
     220         333 : {
     221         333 :     GCSFilter::Params params;
     222         333 :     if (!BuildParams(params)) {
     223           0 :         throw std::invalid_argument("unknown filter_type");
     224             :     }
     225         333 :     m_filter = GCSFilter(params, std::move(filter), skip_decode_check);
     226         666 : }
     227             : 
     228         702 : BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo)
     229         234 :     : m_filter_type(filter_type), m_block_hash(block.GetHash())
     230         234 : {
     231         234 :     GCSFilter::Params params;
     232         234 :     if (!BuildParams(params)) {
     233           0 :         throw std::invalid_argument("unknown filter_type");
     234             :     }
     235         234 :     m_filter = GCSFilter(params, BasicFilterElements(block, block_undo));
     236         468 : }
     237             : 
     238         568 : bool BlockFilter::BuildParams(GCSFilter::Params& params) const
     239             : {
     240         568 :     switch (m_filter_type) {
     241             :     case BlockFilterType::BASIC_FILTER:
     242         568 :         params.m_siphash_k0 = m_block_hash.GetUint64(0);
     243         568 :         params.m_siphash_k1 = m_block_hash.GetUint64(1);
     244         568 :         params.m_P = BASIC_FILTER_P;
     245         568 :         params.m_M = BASIC_FILTER_M;
     246         568 :         return true;
     247             :     case BlockFilterType::INVALID:
     248           0 :         return false;
     249             :     }
     250             : 
     251           0 :     return false;
     252         568 : }
     253             : 
     254         911 : uint256 BlockFilter::GetHash() const
     255             : {
     256         911 :     return Hash(GetEncodedFilter());
     257             : }
     258             : 
     259         231 : uint256 BlockFilter::ComputeHeader(const uint256& prev_header) const
     260             : {
     261         231 :     return Hash(GetHash(), prev_header);
     262             : }

Generated by: LCOV version 1.16