blockfilter.cpp raw

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