LCOV - code coverage report
Current view: top level - src - blockfilter.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 172 187 92.0 %
Date: 2021-06-29 14:35:33 Functions: 21 21 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 49 58 84.5 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2018-2020 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                 :            : static const std::map<BlockFilterType, std::string> g_filter_types = {
#      24                 :            :     {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                 :            : static uint64_t MapIntoRange(uint64_t x, uint64_t n)
#      33                 :      26038 : {
#      34                 :      26038 : #ifdef __SIZEOF_INT128__
#      35                 :      26038 :     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                 :      26038 : }
#      57                 :            : 
#      58                 :            : uint64_t GCSFilter::HashToRange(const Element& element) const
#      59                 :      26038 : {
#      60                 :      26038 :     uint64_t hash = CSipHasher(m_params.m_siphash_k0, m_params.m_siphash_k1)
#      61                 :      26038 :         .Write(element.data(), element.size())
#      62                 :      26038 :         .Finalize();
#      63                 :      26038 :     return MapIntoRange(hash, m_F);
#      64                 :      26038 : }
#      65                 :            : 
#      66                 :            : std::vector<uint64_t> GCSFilter::BuildHashedSet(const ElementSet& elements) const
#      67                 :       5584 : {
#      68                 :       5584 :     std::vector<uint64_t> hashed_elements;
#      69                 :       5584 :     hashed_elements.reserve(elements.size());
#      70         [ +  + ]:      25820 :     for (const Element& element : elements) {
#      71                 :      25820 :         hashed_elements.push_back(HashToRange(element));
#      72                 :      25820 :     }
#      73                 :       5584 :     std::sort(hashed_elements.begin(), hashed_elements.end());
#      74                 :       5584 :     return hashed_elements;
#      75                 :       5584 : }
#      76                 :            : 
#      77                 :            : GCSFilter::GCSFilter(const Params& params)
#      78                 :            :     : m_params(params), m_N(0), m_F(0), m_encoded{0}
#      79                 :       7004 : {}
#      80                 :            : 
#      81                 :            : GCSFilter::GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter)
#      82                 :            :     : m_params(params), m_encoded(std::move(encoded_filter))
#      83                 :        693 : {
#      84                 :        693 :     VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
#      85                 :            : 
#      86                 :        693 :     uint64_t N = ReadCompactSize(stream);
#      87                 :        693 :     m_N = static_cast<uint32_t>(N);
#      88         [ -  + ]:        693 :     if (m_N != N) {
#      89                 :          0 :         throw std::ios_base::failure("N must be <2^32");
#      90                 :          0 :     }
#      91                 :        693 :     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                 :        693 :     BitStreamReader<VectorReader> bitreader(stream);
#      96         [ +  + ]:       1394 :     for (uint64_t i = 0; i < m_N; ++i) {
#      97                 :        701 :         GolombRiceDecode(bitreader, m_params.m_P);
#      98                 :        701 :     }
#      99         [ -  + ]:        693 :     if (!stream.empty()) {
#     100                 :          0 :         throw std::ios_base::failure("encoded_filter contains excess data");
#     101                 :          0 :     }
#     102                 :        693 : }
#     103                 :            : 
#     104                 :            : GCSFilter::GCSFilter(const Params& params, const ElementSet& elements)
#     105                 :            :     : m_params(params)
#     106                 :       5386 : {
#     107                 :       5386 :     size_t N = elements.size();
#     108                 :       5386 :     m_N = static_cast<uint32_t>(N);
#     109         [ -  + ]:       5386 :     if (m_N != N) {
#     110                 :          0 :         throw std::invalid_argument("N must be <2^32");
#     111                 :          0 :     }
#     112                 :       5386 :     m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M);
#     113                 :            : 
#     114                 :       5386 :     CVectorWriter stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
#     115                 :            : 
#     116                 :       5386 :     WriteCompactSize(stream, m_N);
#     117                 :            : 
#     118         [ +  + ]:       5386 :     if (elements.empty()) {
#     119                 :          2 :         return;
#     120                 :          2 :     }
#     121                 :            : 
#     122                 :       5384 :     BitStreamWriter<CVectorWriter> bitwriter(stream);
#     123                 :            : 
#     124                 :       5384 :     uint64_t last_value = 0;
#     125         [ +  + ]:       5652 :     for (uint64_t value : BuildHashedSet(elements)) {
#     126                 :       5652 :         uint64_t delta = value - last_value;
#     127                 :       5652 :         GolombRiceEncode(bitwriter, m_params.m_P, delta);
#     128                 :       5652 :         last_value = value;
#     129                 :       5652 :     }
#     130                 :            : 
#     131                 :       5384 :     bitwriter.Flush();
#     132                 :       5384 : }
#     133                 :            : 
#     134                 :            : bool GCSFilter::MatchInternal(const uint64_t* element_hashes, size_t size) const
#     135                 :        418 : {
#     136                 :        418 :     VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
#     137                 :            : 
#     138                 :            :     // Seek forward by size of N
#     139                 :        418 :     uint64_t N = ReadCompactSize(stream);
#     140                 :        418 :     assert(N == m_N);
#     141                 :            : 
#     142                 :        418 :     BitStreamReader<VectorReader> bitreader(stream);
#     143                 :            : 
#     144                 :        418 :     uint64_t value = 0;
#     145                 :        418 :     size_t hashes_index = 0;
#     146         [ +  + ]:      18150 :     for (uint32_t i = 0; i < m_N; ++i) {
#     147                 :      18148 :         uint64_t delta = GolombRiceDecode(bitreader, m_params.m_P);
#     148                 :      18148 :         value += delta;
#     149                 :            : 
#     150                 :      25882 :         while (true) {
#     151         [ +  + ]:      25882 :             if (hashes_index == size) {
#     152                 :          6 :                 return false;
#     153         [ +  + ]:      25876 :             } else if (element_hashes[hashes_index] == value) {
#     154                 :        410 :                 return true;
#     155         [ +  + ]:      25466 :             } else if (element_hashes[hashes_index] > value) {
#     156                 :      17732 :                 break;
#     157                 :      17732 :             }
#     158                 :            : 
#     159                 :       7734 :             hashes_index++;
#     160                 :       7734 :         }
#     161                 :      18148 :     }
#     162                 :            : 
#     163                 :        418 :     return false;
#     164                 :        418 : }
#     165                 :            : 
#     166                 :            : bool GCSFilter::Match(const Element& element) const
#     167                 :        218 : {
#     168                 :        218 :     uint64_t query = HashToRange(element);
#     169                 :        218 :     return MatchInternal(&query, 1);
#     170                 :        218 : }
#     171                 :            : 
#     172                 :            : bool GCSFilter::MatchAny(const ElementSet& elements) const
#     173                 :        200 : {
#     174                 :        200 :     const std::vector<uint64_t> queries = BuildHashedSet(elements);
#     175                 :        200 :     return MatchInternal(queries.data(), queries.size());
#     176                 :        200 : }
#     177                 :            : 
#     178                 :            : const std::string& BlockFilterTypeName(BlockFilterType filter_type)
#     179                 :         26 : {
#     180                 :         26 :     static std::string unknown_retval = "";
#     181                 :         26 :     auto it = g_filter_types.find(filter_type);
#     182         [ +  + ]:         26 :     return it != g_filter_types.end() ? it->second : unknown_retval;
#     183                 :         26 : }
#     184                 :            : 
#     185                 :         22 : bool BlockFilterTypeByName(const std::string& name, BlockFilterType& filter_type) {
#     186         [ +  + ]:         22 :     for (const auto& entry : g_filter_types) {
#     187         [ +  + ]:         22 :         if (entry.second == name) {
#     188                 :         19 :             filter_type = entry.first;
#     189                 :         19 :             return true;
#     190                 :         19 :         }
#     191                 :         22 :     }
#     192                 :         22 :     return false;
#     193                 :         22 : }
#     194                 :            : 
#     195                 :            : const std::set<BlockFilterType>& AllBlockFilterTypes()
#     196                 :          7 : {
#     197                 :          7 :     static std::set<BlockFilterType> types;
#     198                 :            : 
#     199                 :          7 :     static std::once_flag flag;
#     200                 :          7 :     std::call_once(flag, []() {
#     201         [ +  + ]:          7 :             for (auto entry : g_filter_types) {
#     202                 :          7 :                 types.insert(entry.first);
#     203                 :          7 :             }
#     204                 :          7 :         });
#     205                 :            : 
#     206                 :          7 :     return types;
#     207                 :          7 : }
#     208                 :            : 
#     209                 :            : const std::string& ListBlockFilterTypes()
#     210                 :       1536 : {
#     211                 :       1536 :     static std::string type_list;
#     212                 :            : 
#     213                 :       1536 :     static std::once_flag flag;
#     214                 :       1536 :     std::call_once(flag, []() {
#     215                 :        690 :             std::stringstream ret;
#     216                 :        690 :             bool first = true;
#     217         [ +  + ]:        690 :             for (auto entry : g_filter_types) {
#     218         [ -  + ]:        690 :                 if (!first) ret << ", ";
#     219                 :        690 :                 ret << entry.second;
#     220                 :        690 :                 first = false;
#     221                 :        690 :             }
#     222                 :        690 :             type_list = ret.str();
#     223                 :        690 :         });
#     224                 :            : 
#     225                 :       1536 :     return type_list;
#     226                 :       1536 : }
#     227                 :            : 
#     228                 :            : static GCSFilter::ElementSet BasicFilterElements(const CBlock& block,
#     229                 :            :                                                  const CBlockUndo& block_undo)
#     230                 :       5384 : {
#     231                 :       5384 :     GCSFilter::ElementSet elements;
#     232                 :            : 
#     233         [ +  + ]:       5406 :     for (const CTransactionRef& tx : block.vtx) {
#     234         [ +  + ]:      10786 :         for (const CTxOut& txout : tx->vout) {
#     235                 :      10786 :             const CScript& script = txout.scriptPubKey;
#     236 [ +  + ][ +  + ]:      10786 :             if (script.empty() || script[0] == OP_RETURN) continue;
#     237                 :       5420 :             elements.emplace(script.begin(), script.end());
#     238                 :       5420 :         }
#     239                 :       5406 :     }
#     240                 :            : 
#     241         [ +  + ]:       5384 :     for (const CTxUndo& tx_undo : block_undo.vtxundo) {
#     242         [ +  + ]:         56 :         for (const Coin& prevout : tx_undo.vprevout) {
#     243                 :         56 :             const CScript& script = prevout.out.scriptPubKey;
#     244         [ +  + ]:         56 :             if (script.empty()) continue;
#     245                 :         48 :             elements.emplace(script.begin(), script.end());
#     246                 :         48 :         }
#     247                 :         22 :     }
#     248                 :            : 
#     249                 :       5384 :     return elements;
#     250                 :       5384 : }
#     251                 :            : 
#     252                 :            : BlockFilter::BlockFilter(BlockFilterType filter_type, const uint256& block_hash,
#     253                 :            :                          std::vector<unsigned char> filter)
#     254                 :            :     : m_filter_type(filter_type), m_block_hash(block_hash)
#     255                 :        691 : {
#     256                 :        691 :     GCSFilter::Params params;
#     257         [ -  + ]:        691 :     if (!BuildParams(params)) {
#     258                 :          0 :         throw std::invalid_argument("unknown filter_type");
#     259                 :          0 :     }
#     260                 :        691 :     m_filter = GCSFilter(params, std::move(filter));
#     261                 :        691 : }
#     262                 :            : 
#     263                 :            : BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo)
#     264                 :            :     : m_filter_type(filter_type), m_block_hash(block.GetHash())
#     265                 :       5384 : {
#     266                 :       5384 :     GCSFilter::Params params;
#     267         [ -  + ]:       5384 :     if (!BuildParams(params)) {
#     268                 :          0 :         throw std::invalid_argument("unknown filter_type");
#     269                 :          0 :     }
#     270                 :       5384 :     m_filter = GCSFilter(params, BasicFilterElements(block, block_undo));
#     271                 :       5384 : }
#     272                 :            : 
#     273                 :            : bool BlockFilter::BuildParams(GCSFilter::Params& params) const
#     274                 :       6077 : {
#     275         [ -  + ]:       6077 :     switch (m_filter_type) {
#     276         [ +  - ]:       6077 :     case BlockFilterType::BASIC:
#     277                 :       6077 :         params.m_siphash_k0 = m_block_hash.GetUint64(0);
#     278                 :       6077 :         params.m_siphash_k1 = m_block_hash.GetUint64(1);
#     279                 :       6077 :         params.m_P = BASIC_FILTER_P;
#     280                 :       6077 :         params.m_M = BASIC_FILTER_M;
#     281                 :       6077 :         return true;
#     282         [ -  + ]:          0 :     case BlockFilterType::INVALID:
#     283                 :          0 :         return false;
#     284                 :          0 :     }
#     285                 :            : 
#     286                 :          0 :     return false;
#     287                 :          0 : }
#     288                 :            : 
#     289                 :            : uint256 BlockFilter::GetHash() const
#     290                 :      11656 : {
#     291                 :      11656 :     const std::vector<unsigned char>& data = GetEncodedFilter();
#     292                 :            : 
#     293                 :      11656 :     uint256 result;
#     294                 :      11656 :     CHash256().Write(data).Finalize(result);
#     295                 :      11656 :     return result;
#     296                 :      11656 : }
#     297                 :            : 
#     298                 :            : uint256 BlockFilter::ComputeHeader(const uint256& prev_header) const
#     299                 :       5382 : {
#     300                 :       5382 :     const uint256& filter_hash = GetHash();
#     301                 :            : 
#     302                 :       5382 :     uint256 result;
#     303                 :       5382 :     CHash256()
#     304                 :       5382 :         .Write(filter_hash)
#     305                 :       5382 :         .Write(prev_header)
#     306                 :       5382 :         .Finalize(result);
#     307                 :       5382 :     return result;
#     308                 :       5382 : }

Generated by: LCOV version 1.14