Bitcoin Core  26.1.0
P2P Digital Currency
blockfilter.cpp
Go to the documentation of this file.
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 
20 static constexpr int GCS_SER_VERSION = 0;
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 {
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 
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 {
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<SpanReader> 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 
85 
86  WriteCompactSize(stream, m_N);
87 
88  if (elements.empty()) {
89  return;
90  }
91 
92  BitStreamWriter<CVectorWriter> 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 {
107 
108  // Seek forward by size of N
109  uint64_t N = ReadCompactSize(stream);
110  assert(N == m_N);
111 
112  BitStreamReader<SpanReader> 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(const std::string& name, BlockFilterType& filter_type) {
156  for (const auto& entry : g_filter_types) {
157  if (entry.second == name) {
158  filter_type = entry.first;
159  return true;
160  }
161  }
162  return false;
163 }
164 
165 const std::set<BlockFilterType>& AllBlockFilterTypes()
166 {
167  static std::set<BlockFilterType> types;
168 
169  static std::once_flag flag;
170  std::call_once(flag, []() {
171  for (const auto& entry : g_filter_types) {
172  types.insert(entry.first);
173  }
174  });
175 
176  return types;
177 }
178 
179 const std::string& ListBlockFilterTypes()
180 {
181  static std::string type_list{Join(g_filter_types, ", ", [](const auto& entry) { return entry.second; })};
182 
183  return type_list;
184 }
185 
187  const CBlockUndo& block_undo)
188 {
189  GCSFilter::ElementSet elements;
190 
191  for (const CTransactionRef& tx : block.vtx) {
192  for (const CTxOut& txout : tx->vout) {
193  const CScript& script = txout.scriptPubKey;
194  if (script.empty() || script[0] == OP_RETURN) continue;
195  elements.emplace(script.begin(), script.end());
196  }
197  }
198 
199  for (const CTxUndo& tx_undo : block_undo.vtxundo) {
200  for (const Coin& prevout : tx_undo.vprevout) {
201  const CScript& script = prevout.out.scriptPubKey;
202  if (script.empty()) continue;
203  elements.emplace(script.begin(), script.end());
204  }
205  }
206 
207  return elements;
208 }
209 
210 BlockFilter::BlockFilter(BlockFilterType filter_type, const uint256& block_hash,
211  std::vector<unsigned char> filter, bool skip_decode_check)
212  : m_filter_type(filter_type), m_block_hash(block_hash)
213 {
214  GCSFilter::Params params;
215  if (!BuildParams(params)) {
216  throw std::invalid_argument("unknown filter_type");
217  }
218  m_filter = GCSFilter(params, std::move(filter), skip_decode_check);
219 }
220 
221 BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo)
222  : m_filter_type(filter_type), m_block_hash(block.GetHash())
223 {
224  GCSFilter::Params params;
225  if (!BuildParams(params)) {
226  throw std::invalid_argument("unknown filter_type");
227  }
228  m_filter = GCSFilter(params, BasicFilterElements(block, block_undo));
229 }
230 
232 {
233  switch (m_filter_type) {
235  params.m_siphash_k0 = m_block_hash.GetUint64(0);
236  params.m_siphash_k1 = m_block_hash.GetUint64(1);
237  params.m_P = BASIC_FILTER_P;
238  params.m_M = BASIC_FILTER_M;
239  return true;
241  return false;
242  }
243 
244  return false;
245 }
246 
248 {
249  return Hash(GetEncodedFilter());
250 }
251 
252 uint256 BlockFilter::ComputeHeader(const uint256& prev_header) const
253 {
254  return Hash(GetHash(), prev_header);
255 }
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:421
std::vector< Coin > vprevout
Definition: undo.h:57
BlockFilter()=default
assert(!tx.IsCoinBase())
constexpr uint32_t BASIC_FILTER_M
Definition: blockfilter.h:90
CScript scriptPubKey
Definition: transaction.h:161
CSipHasher & Write(uint64_t data)
Hash a 64-bit integer worth of data It is treated as if this was the little-endian interpretation of ...
Definition: siphash.cpp:28
A UTXO entry.
Definition: coins.h:31
Definition: block.h:68
bool BlockFilterTypeByName(const std::string &name, BlockFilterType &filter_type)
Find a filter type by its human-readable name.
uint64_t ReadCompactSize(Stream &is, bool range_check=true)
Decode a CompactSize-encoded variable-length integer.
Definition: serialize.h:361
void WriteCompactSize(CSizeComputer &os, uint64_t nSize)
Definition: serialize.h:1121
static uint64_t FastRange64(uint64_t x, uint64_t n)
Fast range reduction with 64-bit input and 64-bit range.
Definition: fastrange.h:25
CTxOut out
unspent transaction output
Definition: coins.h:35
constexpr uint8_t BASIC_FILTER_P
Definition: blockfilter.h:89
bool MatchAny(const ElementSet &elements) const
Checks if any of the given elements may be in the set.
const std::set< BlockFilterType > & AllBlockFilterTypes()
Get a list of known filter types.
std::vector< uint64_t > BuildHashedSet(const ElementSet &elements) const
Definition: blockfilter.cpp:34
GCSFilter m_filter
Definition: blockfilter.h:119
static const std::map< BlockFilterType, std::string > g_filter_types
Definition: blockfilter.cpp:22
uint256 GetHash() const
Compute the filter hash.
bool Match(const Element &element) const
Checks if the element may be in the set.
GCSFilter(const Params &params=Params())
Constructs an empty filter.
Definition: blockfilter.cpp:45
Minimal stream for reading from an existing byte array by Span.
Definition: streams.h:146
bool MatchInternal(const uint64_t *sorted_element_hashes, size_t size) const
Helper method used to implement Match and MatchAny.
iterator end()
Definition: prevector.h:301
BlockFilterType
Definition: blockfilter.h:92
uint64_t GolombRiceDecode(BitStreamReader< IStream > &bitreader, uint8_t P)
Definition: golombrice.h:32
const std::string & ListBlockFilterTypes()
Get a comma-separated list of known filter type names.
const char * name
Definition: rest.cpp:45
uint64_t HashToRange(const Element &element) const
Hash a data element to an integer in the range [0, N * M).
Definition: blockfilter.cpp:26
An output of a transaction.
Definition: transaction.h:157
uint64_t Finalize() const
Compute the 64-bit SipHash-2-4 of the data written so far.
Definition: siphash.cpp:77
static GCSFilter::ElementSet BasicFilterElements(const CBlock &block, const CBlockUndo &block_undo)
This implements a Golomb-coded set as defined in BIP 158.
Definition: blockfilter.h:28
void Flush()
Flush any unwritten bits to the output stream, padding with 0&#39;s to the next byte boundary.
Definition: streams.h:453
std::unordered_set< Element, ByteVectorHash > ElementSet
Definition: blockfilter.h:32
uint32_t m_M
Inverse false positive rate.
Definition: blockfilter.h:39
uint256 ComputeHeader(const uint256 &prev_header) const
Compute the filter header given the previous one.
uint64_t m_siphash_k0
Definition: blockfilter.h:36
uint8_t m_P
Golomb-Rice coding parameter.
Definition: blockfilter.h:38
256-bit opaque blob.
Definition: uint256.h:106
std::vector< CTransactionRef > vtx
Definition: block.h:72
Undo information for a CBlock.
Definition: undo.h:63
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:412
Undo information for a CTransaction.
Definition: undo.h:53
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition: string.h:68
BlockFilterType m_filter_type
Definition: blockfilter.h:117
bool empty() const
Definition: prevector.h:295
constexpr uint64_t GetUint64(int pos) const
Definition: uint256.h:76
void GolombRiceEncode(BitStreamWriter< OStream > &bitwriter, uint8_t P, uint64_t x)
Definition: golombrice.h:15
SipHash-2-4.
Definition: siphash.h:14
uint256 m_block_hash
Definition: blockfilter.h:118
Params m_params
Definition: blockfilter.h:47
iterator begin()
Definition: prevector.h:299
std::vector< unsigned char > Element
Definition: blockfilter.h:31
static constexpr int GCS_SER_VERSION
Protocol version used to serialize parameters in GCS filter encoding.
Definition: blockfilter.cpp:20
uint256 Hash(const T &in1)
Compute the 256-bit hash of an object.
Definition: hash.h:76
const std::vector< unsigned char > & GetEncodedFilter() const LIFETIMEBOUND
Definition: blockfilter.h:138
uint64_t m_siphash_k1
Definition: blockfilter.h:37
uint32_t m_N
Number of elements in the filter.
Definition: blockfilter.h:48
std::vector< CTxUndo > vtxundo
Definition: undo.h:66
uint64_t m_F
Range of element hashes, F = N * M.
Definition: blockfilter.h:49
bool BuildParams(GCSFilter::Params &params) const
std::vector< unsigned char > m_encoded
Definition: blockfilter.h:50
const std::string & BlockFilterTypeName(BlockFilterType filter_type)
Get the human-readable name for a filter type.