LCOV - code coverage report
Current view: top level - src - blockfilter.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 9 145 6.2 %
Date: 2023-09-26 12:08:55 Functions: 4 21 19.0 %

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

Generated by: LCOV version 1.14