LCOV - code coverage report
Current view: top level - src - blockfilter.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 158 166 95.2 %
Date: 2020-09-26 01:30:44 Functions: 27 27 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2018-2019 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 <sstream>
       7             : #include <set>
       8             : 
       9             : #include <blockfilter.h>
      10             : #include <crypto/siphash.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             : 
      17             : /// SerType used to serialize parameters in GCS filter encoding.
      18             : static constexpr int GCS_SER_TYPE = SER_NETWORK;
      19             : 
      20             : /// Protocol version used to serialize parameters in GCS filter encoding.
      21             : static constexpr int GCS_SER_VERSION = 0;
      22             : 
      23         640 : static const std::map<BlockFilterType, std::string> g_filter_types = {
      24         640 :     {BlockFilterType::BASIC, "basic"},
      25             : };
      26             : 
      27             : // Map a value x that is uniformly distributed in the range [0, 2^64) to a
      28             : // value uniformly distributed in [0, n) by returning the upper 64 bits of
      29             : // x * n.
      30             : //
      31             : // See: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
      32      134778 : static uint64_t MapIntoRange(uint64_t x, uint64_t n)
      33             : {
      34             : #ifdef __SIZEOF_INT128__
      35      134778 :     return (static_cast<unsigned __int128>(x) * static_cast<unsigned __int128>(n)) >> 64;
      36             : #else
      37             :     // To perform the calculation on 64-bit numbers without losing the
      38             :     // result to overflow, split the numbers into the most significant and
      39             :     // least significant 32 bits and perform multiplication piece-wise.
      40             :     //
      41             :     // See: https://stackoverflow.com/a/26855440
      42             :     uint64_t x_hi = x >> 32;
      43             :     uint64_t x_lo = x & 0xFFFFFFFF;
      44             :     uint64_t n_hi = n >> 32;
      45             :     uint64_t n_lo = n & 0xFFFFFFFF;
      46             : 
      47             :     uint64_t ac = x_hi * n_hi;
      48             :     uint64_t ad = x_hi * n_lo;
      49             :     uint64_t bc = x_lo * n_hi;
      50             :     uint64_t bd = x_lo * n_lo;
      51             : 
      52             :     uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
      53             :     uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
      54             :     return upper64;
      55             : #endif
      56             : }
      57             : 
      58      134778 : uint64_t GCSFilter::HashToRange(const Element& element) const
      59             : {
      60      134778 :     uint64_t hash = CSipHasher(m_params.m_siphash_k0, m_params.m_siphash_k1)
      61      134778 :         .Write(element.data(), element.size())
      62      134778 :         .Finalize();
      63      134778 :     return MapIntoRange(hash, m_F);
      64             : }
      65             : 
      66        4559 : std::vector<uint64_t> GCSFilter::BuildHashedSet(const ElementSet& elements) const
      67             : {
      68        4559 :     std::vector<uint64_t> hashed_elements;
      69        4559 :     hashed_elements.reserve(elements.size());
      70      139217 :     for (const Element& element : elements) {
      71      134658 :         hashed_elements.push_back(HashToRange(element));
      72           0 :     }
      73        4559 :     std::sort(hashed_elements.begin(), hashed_elements.end());
      74             :     return hashed_elements;
      75        4559 : }
      76             : 
      77       10552 : GCSFilter::GCSFilter(const Params& params)
      78        5276 :     : m_params(params), m_N(0), m_F(0), m_encoded{0}
      79       10552 : {}
      80             : 
      81         712 : GCSFilter::GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter)
      82         356 :     : m_params(params), m_encoded(std::move(encoded_filter))
      83         356 : {
      84         356 :     VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
      85             : 
      86         356 :     uint64_t N = ReadCompactSize(stream);
      87         356 :     m_N = static_cast<uint32_t>(N);
      88         356 :     if (m_N != N) {
      89           0 :         throw std::ios_base::failure("N must be <2^32");
      90             :     }
      91         356 :     m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M);
      92             : 
      93             :     // Verify that the encoded filter contains exactly N elements. If it has too much or too little
      94             :     // data, a std::ios_base::failure exception will be raised.
      95         356 :     BitStreamReader<VectorReader> bitreader(stream);
      96         716 :     for (uint64_t i = 0; i < m_N; ++i) {
      97         360 :         GolombRiceDecode(bitreader, m_params.m_P);
      98             :     }
      99         356 :     if (!stream.empty()) {
     100           0 :         throw std::ios_base::failure("encoded_filter contains excess data");
     101             :     }
     102         712 : }
     103             : 
     104        8920 : GCSFilter::GCSFilter(const Params& params, const ElementSet& elements)
     105        4460 :     : m_params(params)
     106        4460 : {
     107        4460 :     size_t N = elements.size();
     108        4460 :     m_N = static_cast<uint32_t>(N);
     109        4460 :     if (m_N != N) {
     110           0 :         throw std::invalid_argument("N must be <2^32");
     111             :     }
     112        4460 :     m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M);
     113             : 
     114        4460 :     CVectorWriter stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
     115             : 
     116        4460 :     WriteCompactSize(stream, m_N);
     117             : 
     118        4460 :     if (elements.empty()) {
     119           1 :         return;
     120             :     }
     121             : 
     122        4459 :     BitStreamWriter<CVectorWriter> bitwriter(stream);
     123             : 
     124             :     uint64_t last_value = 0;
     125      129040 :     for (uint64_t value : BuildHashedSet(elements)) {
     126      124581 :         uint64_t delta = value - last_value;
     127      124581 :         GolombRiceEncode(bitwriter, m_params.m_P, delta);
     128             :         last_value = value;
     129             :     }
     130             : 
     131        4459 :     bitwriter.Flush();
     132        8920 : }
     133             : 
     134         220 : bool GCSFilter::MatchInternal(const uint64_t* element_hashes, size_t size) const
     135             : {
     136         220 :     VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
     137             : 
     138             :     // Seek forward by size of N
     139         220 :     uint64_t N = ReadCompactSize(stream);
     140         220 :     assert(N == m_N);
     141             : 
     142         220 :     BitStreamReader<VectorReader> bitreader(stream);
     143             : 
     144             :     uint64_t value = 0;
     145       21624 :     size_t hashes_index = 0;
     146       21624 :     for (uint32_t i = 0; i < m_N; ++i) {
     147       21623 :         uint64_t delta = GolombRiceDecode(bitreader, m_params.m_P);
     148       21623 :         value += delta;
     149             : 
     150       25608 :         while (true) {
     151       25608 :             if (hashes_index == size) {
     152          14 :                 return false;
     153       25594 :             } else if (element_hashes[hashes_index] == value) {
     154         205 :                 return true;
     155       25389 :             } else if (element_hashes[hashes_index] > value) {
     156             :                 break;
     157             :             }
     158             : 
     159        3985 :             hashes_index++;
     160             :         }
     161       21404 :     }
     162             : 
     163           1 :     return false;
     164         220 : }
     165             : 
     166         120 : bool GCSFilter::Match(const Element& element) const
     167             : {
     168         120 :     uint64_t query = HashToRange(element);
     169         240 :     return MatchInternal(&query, 1);
     170         120 : }
     171             : 
     172         100 : bool GCSFilter::MatchAny(const ElementSet& elements) const
     173             : {
     174         100 :     const std::vector<uint64_t> queries = BuildHashedSet(elements);
     175         100 :     return MatchInternal(queries.data(), queries.size());
     176         100 : }
     177             : 
     178          14 : const std::string& BlockFilterTypeName(BlockFilterType filter_type)
     179             : {
     180          14 :     static std::string unknown_retval = "";
     181          14 :     auto it = g_filter_types.find(filter_type);
     182          14 :     return it != g_filter_types.end() ? it->second : unknown_retval;
     183          14 : }
     184             : 
     185          15 : bool BlockFilterTypeByName(const std::string& name, BlockFilterType& filter_type) {
     186          30 :     for (const auto& entry : g_filter_types) {
     187          15 :         if (entry.second == name) {
     188          13 :             filter_type = entry.first;
     189          13 :             return true;
     190             :         }
     191           2 :     }
     192           2 :     return false;
     193          15 : }
     194             : 
     195           4 : const std::set<BlockFilterType>& AllBlockFilterTypes()
     196             : {
     197           4 :     static std::set<BlockFilterType> types;
     198             : 
     199             :     static std::once_flag flag;
     200           8 :     std::call_once(flag, []() {
     201           8 :             for (auto entry : g_filter_types) {
     202           4 :                 types.insert(entry.first);
     203           4 :             }
     204           4 :         });
     205             : 
     206           4 :     return types;
     207             : }
     208             : 
     209        1010 : const std::string& ListBlockFilterTypes()
     210             : {
     211        1010 :     static std::string type_list;
     212             : 
     213             :     static std::once_flag flag;
     214        1646 :     std::call_once(flag, []() {
     215         636 :             std::stringstream ret;
     216             :             bool first = true;
     217        1272 :             for (auto entry : g_filter_types) {
     218         636 :                 if (!first) ret << ", ";
     219         636 :                 ret << entry.second;
     220             :                 first = false;
     221         636 :             }
     222         636 :             type_list = ret.str();
     223         636 :         });
     224             : 
     225        1010 :     return type_list;
     226             : }
     227             : 
     228        4447 : static GCSFilter::ElementSet BasicFilterElements(const CBlock& block,
     229             :                                                  const CBlockUndo& block_undo)
     230             : {
     231        4447 :     GCSFilter::ElementSet elements;
     232             : 
     233        8905 :     for (const CTransactionRef& tx : block.vtx) {
     234       13360 :         for (const CTxOut& txout : tx->vout) {
     235        8902 :             const CScript& script = txout.scriptPubKey;
     236        8902 :             if (script.empty() || script[0] == OP_RETURN) continue;
     237        4465 :             elements.emplace(script.begin(), script.end());
     238        8930 :         }
     239             :     }
     240             : 
     241        4458 :     for (const CTxUndo& tx_undo : block_undo.vtxundo) {
     242          39 :         for (const Coin& prevout : tx_undo.vprevout) {
     243          28 :             const CScript& script = prevout.out.scriptPubKey;
     244          28 :             if (script.empty()) continue;
     245          24 :             elements.emplace(script.begin(), script.end());
     246          48 :         }
     247             :     }
     248             : 
     249             :     return elements;
     250        4447 : }
     251             : 
     252         710 : BlockFilter::BlockFilter(BlockFilterType filter_type, const uint256& block_hash,
     253             :                          std::vector<unsigned char> filter)
     254         355 :     : m_filter_type(filter_type), m_block_hash(block_hash)
     255         355 : {
     256         355 :     GCSFilter::Params params;
     257         355 :     if (!BuildParams(params)) {
     258           0 :         throw std::invalid_argument("unknown filter_type");
     259             :     }
     260         355 :     m_filter = GCSFilter(params, std::move(filter));
     261         710 : }
     262             : 
     263        8894 : BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo)
     264        4447 :     : m_filter_type(filter_type), m_block_hash(block.GetHash())
     265        4447 : {
     266        4447 :     GCSFilter::Params params;
     267        4447 :     if (!BuildParams(params)) {
     268           0 :         throw std::invalid_argument("unknown filter_type");
     269             :     }
     270        4447 :     m_filter = GCSFilter(params, BasicFilterElements(block, block_undo));
     271        8894 : }
     272             : 
     273        4803 : bool BlockFilter::BuildParams(GCSFilter::Params& params) const
     274             : {
     275        4803 :     switch (m_filter_type) {
     276             :     case BlockFilterType::BASIC:
     277        4803 :         params.m_siphash_k0 = m_block_hash.GetUint64(0);
     278        4803 :         params.m_siphash_k1 = m_block_hash.GetUint64(1);
     279        4803 :         params.m_P = BASIC_FILTER_P;
     280        4803 :         params.m_M = BASIC_FILTER_M;
     281        4803 :         return true;
     282             :     case BlockFilterType::INVALID:
     283           0 :         return false;
     284             :     }
     285             : 
     286           0 :     return false;
     287        4803 : }
     288             : 
     289        9338 : uint256 BlockFilter::GetHash() const
     290             : {
     291        9338 :     const std::vector<unsigned char>& data = GetEncodedFilter();
     292             : 
     293        9338 :     uint256 result;
     294        9338 :     CHash256().Write(data).Finalize(result);
     295             :     return result;
     296        9338 : }
     297             : 
     298        4446 : uint256 BlockFilter::ComputeHeader(const uint256& prev_header) const
     299             : {
     300        4446 :     const uint256& filter_hash = GetHash();
     301             : 
     302        4446 :     uint256 result;
     303        8892 :     CHash256()
     304        4446 :         .Write(filter_hash)
     305        4446 :         .Write(prev_header)
     306        4446 :         .Finalize(result);
     307             :     return result;
     308        4446 : }

Generated by: LCOV version 1.15