BSHA3  0.17.99
P2P Blockchain, based on Bitcoin
blockfilter.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018 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 <blockfilter.h>
6 #include <hash.h>
8 #include <script/script.h>
9 #include <streams.h>
10 
12 static constexpr int GCS_SER_TYPE = SER_NETWORK;
13 
15 static constexpr int GCS_SER_VERSION = 0;
16 
17 template <typename OStream>
18 static void GolombRiceEncode(BitStreamWriter<OStream>& bitwriter, uint8_t P, uint64_t x)
19 {
20  // Write quotient as unary-encoded: q 1's followed by one 0.
21  uint64_t q = x >> P;
22  while (q > 0) {
23  int nbits = q <= 64 ? static_cast<int>(q) : 64;
24  bitwriter.Write(~0ULL, nbits);
25  q -= nbits;
26  }
27  bitwriter.Write(0, 1);
28 
29  // Write the remainder in P bits. Since the remainder is just the bottom
30  // P bits of x, there is no need to mask first.
31  bitwriter.Write(x, P);
32 }
33 
34 template <typename IStream>
35 static uint64_t GolombRiceDecode(BitStreamReader<IStream>& bitreader, uint8_t P)
36 {
37  // Read unary-encoded quotient: q 1's followed by one 0.
38  uint64_t q = 0;
39  while (bitreader.Read(1) == 1) {
40  ++q;
41  }
42 
43  uint64_t r = bitreader.Read(P);
44 
45  return (q << P) + r;
46 }
47 
48 // Map a value x that is uniformly distributed in the range [0, 2^64) to a
49 // value uniformly distributed in [0, n) by returning the upper 64 bits of
50 // x * n.
51 //
52 // See: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
53 static uint64_t MapIntoRange(uint64_t x, uint64_t n)
54 {
55 #ifdef __SIZEOF_INT128__
56  return (static_cast<unsigned __int128>(x) * static_cast<unsigned __int128>(n)) >> 64;
57 #else
58  // To perform the calculation on 64-bit numbers without losing the
59  // result to overflow, split the numbers into the most significant and
60  // least significant 32 bits and perform multiplication piece-wise.
61  //
62  // See: https://stackoverflow.com/a/26855440
63  uint64_t x_hi = x >> 32;
64  uint64_t x_lo = x & 0xFFFFFFFF;
65  uint64_t n_hi = n >> 32;
66  uint64_t n_lo = n & 0xFFFFFFFF;
67 
68  uint64_t ac = x_hi * n_hi;
69  uint64_t ad = x_hi * n_lo;
70  uint64_t bc = x_lo * n_hi;
71  uint64_t bd = x_lo * n_lo;
72 
73  uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
74  uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
75  return upper64;
76 #endif
77 }
78 
79 uint64_t GCSFilter::HashToRange(const Element& element) const
80 {
81  uint64_t hash = CSipHasher(m_siphash_k0, m_siphash_k1)
82  .Write(element.data(), element.size())
83  .Finalize();
84  return MapIntoRange(hash, m_F);
85 }
86 
87 std::vector<uint64_t> GCSFilter::BuildHashedSet(const ElementSet& elements) const
88 {
89  std::vector<uint64_t> hashed_elements;
90  hashed_elements.reserve(elements.size());
91  for (const Element& element : elements) {
92  hashed_elements.push_back(HashToRange(element));
93  }
94  std::sort(hashed_elements.begin(), hashed_elements.end());
95  return hashed_elements;
96 }
97 
98 GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M)
99  : m_siphash_k0(siphash_k0), m_siphash_k1(siphash_k1), m_P(P), m_M(M), m_N(0), m_F(0)
100 {}
101 
102 GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M,
103  std::vector<unsigned char> encoded_filter)
104  : GCSFilter(siphash_k0, siphash_k1, P, M)
105 {
106  m_encoded = std::move(encoded_filter);
107 
108  VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
109 
110  uint64_t N = ReadCompactSize(stream);
111  m_N = static_cast<uint32_t>(N);
112  if (m_N != N) {
113  throw std::ios_base::failure("N must be <2^32");
114  }
115  m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_M);
116 
117  // Verify that the encoded filter contains exactly N elements. If it has too much or too little
118  // data, a std::ios_base::failure exception will be raised.
119  BitStreamReader<VectorReader> bitreader(stream);
120  for (uint64_t i = 0; i < m_N; ++i) {
121  GolombRiceDecode(bitreader, m_P);
122  }
123  if (!stream.empty()) {
124  throw std::ios_base::failure("encoded_filter contains excess data");
125  }
126 }
127 
128 GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M,
129  const ElementSet& elements)
130  : GCSFilter(siphash_k0, siphash_k1, P, M)
131 {
132  size_t N = elements.size();
133  m_N = static_cast<uint32_t>(N);
134  if (m_N != N) {
135  throw std::invalid_argument("N must be <2^32");
136  }
137  m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_M);
138 
139  CVectorWriter stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
140 
141  WriteCompactSize(stream, m_N);
142 
143  if (elements.empty()) {
144  return;
145  }
146 
147  BitStreamWriter<CVectorWriter> bitwriter(stream);
148 
149  uint64_t last_value = 0;
150  for (uint64_t value : BuildHashedSet(elements)) {
151  uint64_t delta = value - last_value;
152  GolombRiceEncode(bitwriter, m_P, delta);
153  last_value = value;
154  }
155 
156  bitwriter.Flush();
157 }
158 
159 bool GCSFilter::MatchInternal(const uint64_t* element_hashes, size_t size) const
160 {
161  VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0);
162 
163  // Seek forward by size of N
164  uint64_t N = ReadCompactSize(stream);
165  assert(N == m_N);
166 
167  BitStreamReader<VectorReader> bitreader(stream);
168 
169  uint64_t value = 0;
170  size_t hashes_index = 0;
171  for (uint32_t i = 0; i < m_N; ++i) {
172  uint64_t delta = GolombRiceDecode(bitreader, m_P);
173  value += delta;
174 
175  while (true) {
176  if (hashes_index == size) {
177  return false;
178  } else if (element_hashes[hashes_index] == value) {
179  return true;
180  } else if (element_hashes[hashes_index] > value) {
181  break;
182  }
183 
184  hashes_index++;
185  }
186  }
187 
188  return false;
189 }
190 
191 bool GCSFilter::Match(const Element& element) const
192 {
193  uint64_t query = HashToRange(element);
194  return MatchInternal(&query, 1);
195 }
196 
197 bool GCSFilter::MatchAny(const ElementSet& elements) const
198 {
199  const std::vector<uint64_t> queries = BuildHashedSet(elements);
200  return MatchInternal(queries.data(), queries.size());
201 }
202 
203 static GCSFilter::ElementSet BasicFilterElements(const CBlock& block,
204  const CBlockUndo& block_undo)
205 {
206  GCSFilter::ElementSet elements;
207 
208  for (const CTransactionRef& tx : block.vtx) {
209  for (const CTxOut& txout : tx->vout) {
210  const CScript& script = txout.scriptPubKey;
211  if (script.empty() || script[0] == OP_RETURN) continue;
212  elements.emplace(script.begin(), script.end());
213  }
214  }
215 
216  for (const CTxUndo& tx_undo : block_undo.vtxundo) {
217  for (const Coin& prevout : tx_undo.vprevout) {
218  const CScript& script = prevout.out.scriptPubKey;
219  if (script.empty()) continue;
220  elements.emplace(script.begin(), script.end());
221  }
222  }
223 
224  return elements;
225 }
226 
227 BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo)
228  : m_filter_type(filter_type), m_block_hash(block.GetHash())
229 {
230  switch (m_filter_type) {
234  BasicFilterElements(block, block_undo));
235  break;
236 
237  default:
238  throw std::invalid_argument("unknown filter_type");
239  }
240 }
241 
243 {
244  const std::vector<unsigned char>& data = GetEncodedFilter();
245 
246  uint256 result;
247  CHash256().Write(data.data(), data.size()).Finalize(result.begin());
248  return result;
249 }
250 
251 uint256 BlockFilter::ComputeHeader(const uint256& prev_header) const
252 {
253  const uint256& filter_hash = GetHash();
254 
255  uint256 result;
256  CHash256()
257  .Write(filter_hash.begin(), filter_hash.size())
258  .Write(prev_header.begin(), prev_header.size())
259  .Finalize(result.begin());
260  return result;
261 }
std::vector< Coin > vprevout
Definition: undo.h:72
constexpr uint32_t BASIC_FILTER_M
Definition: blockfilter.h:77
CScript scriptPubKey
Definition: transaction.h:135
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: hash.cpp:102
A UTXO entry.
Definition: coins.h:29
Definition: block.h:74
uint64_t ReadCompactSize(Stream &is)
Definition: serialize.h:278
void WriteCompactSize(CSizeComputer &os, uint64_t nSize)
Definition: serialize.h:975
CHash256 & Write(const unsigned char *data, size_t len)
Definition: hash.h:58
CTxOut out
unspent transaction output
Definition: coins.h:33
BlockFilter(BlockFilterType filter_type, const CBlock &block, const CBlockUndo &block_undo)
constexpr uint8_t BASIC_FILTER_P
Definition: blockfilter.h:76
bool MatchAny(const ElementSet &elements) const
Checks if any of the given elements may be in the set.
A hasher class for Bitcoin&#39;s 256-bit hash (double SHA-256).
Definition: hash.h:46
std::vector< uint64_t > BuildHashedSet(const ElementSet &elements) const
Definition: blockfilter.cpp:87
void Write(uint64_t data, int nbits)
Write the nbits least significant bits of a 64-bit int to the output stream.
Definition: streams.h:582
GCSFilter m_filter
Definition: blockfilter.h:93
unsigned char * begin()
Definition: uint256.h:56
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:402
uint256 GetHash() const
bool Match(const Element &element) const
Checks if the element may be in the set.
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:303
BlockFilterType
Definition: blockfilter.h:79
const std::vector< unsigned char > & GetEncodedFilter() const
Definition: blockfilter.h:104
GCSFilter(uint64_t siphash_k0=0, uint64_t siphash_k1=0, uint8_t P=0, uint32_t M=0)
Constructs an empty filter.
Definition: blockfilter.cpp:98
Minimal stream for reading from an existing vector by reference.
Definition: streams.h:144
uint64_t HashToRange(const Element &element) const
Hash a data element to an integer in the range [0, N * M).
Definition: blockfilter.cpp:79
An output of a transaction.
Definition: transaction.h:131
unsigned int size() const
Definition: uint256.h:76
uint64_t m_siphash_k0
Definition: blockfilter.h:28
uint64_t Read(int nbits)
Read the specified number of bits from the stream.
Definition: streams.h:534
This implements a Golomb-coded set as defined in BIP 158.
Definition: blockfilter.h:21
void Flush()
Flush any unwritten bits to the output stream, padding with 0&#39;s to the next byte boundary.
Definition: streams.h:602
uint8_t m_P
Golomb-Rice coding parameter.
Definition: blockfilter.h:30
uint256 ComputeHeader(const uint256 &prev_header) const
256-bit opaque blob.
Definition: uint256.h:122
std::vector< CTransactionRef > vtx
Definition: block.h:78
Undo information for a CBlock.
Definition: undo.h:100
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:384
Undo information for a CTransaction.
Definition: undo.h:68
BlockFilterType m_filter_type
Definition: blockfilter.h:91
bool empty() const
Definition: prevector.h:297
SipHash-2-4.
Definition: hash.h:247
uint256 m_block_hash
Definition: blockfilter.h:92
iterator begin()
Definition: prevector.h:301
std::vector< unsigned char > Element
Definition: blockfilter.h:24
bool empty() const
Definition: streams.h:190
uint32_t m_N
Number of elements in the filter.
Definition: blockfilter.h:32
uint64_t GetUint64(int pos) const
Definition: uint256.h:81
std::vector< CTxUndo > vtxundo
Definition: undo.h:103
uint64_t m_F
Range of element hashes, F = N * M.
Definition: blockfilter.h:33
uint32_t m_M
Inverse false positive rate.
Definition: blockfilter.h:31
std::set< Element > ElementSet
Definition: blockfilter.h:25
uint64_t m_siphash_k1
Definition: blockfilter.h:29
std::vector< unsigned char > m_encoded
Definition: blockfilter.h:34