Bitcoin Core 30.99.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#include <string_view>
8
9#include <blockfilter.h>
10#include <crypto/siphash.h>
11#include <hash.h>
12#include <primitives/block.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
20using util::Join;
21
22static const std::map<BlockFilterType, std::string> g_filter_types = {
23 {BlockFilterType::BASIC, "basic"},
24};
25
26uint64_t GCSFilter::HashToRange(const Element& element) const
27{
29 .Write(element)
30 .Finalize();
31 return FastRange64(hash, m_F);
32}
33
34std::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
49GCSFilter::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
74GCSFilter::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
104bool 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
136bool GCSFilter::Match(const Element& element) const
137{
138 uint64_t query = HashToRange(element);
139 return MatchInternal(&query, 1);
140}
141
142bool 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
148const 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
155bool 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
166const 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
180const 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
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
211BlockFilter::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
222BlockFilter::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
233{
234 switch (m_filter_type) {
238 params.m_P = BASIC_FILTER_P;
239 params.m_M = BASIC_FILTER_M;
240 return true;
242 return false;
243 }
244
245 return false;
246}
247
249{
250 return Hash(GetEncodedFilter());
251}
252
254{
255 return Hash(GetHash(), prev_header);
256}
static const std::map< BlockFilterType, std::string > g_filter_types
Definition: blockfilter.cpp:22
static GCSFilter::ElementSet BasicFilterElements(const CBlock &block, const CBlockUndo &block_undo)
const std::string & BlockFilterTypeName(BlockFilterType filter_type)
Get the human-readable name for a filter type.
const std::set< BlockFilterType > & AllBlockFilterTypes()
Get a list of known filter types.
bool BlockFilterTypeByName(std::string_view name, BlockFilterType &filter_type)
Find a filter type by its human-readable name.
const std::string & ListBlockFilterTypes()
Get a comma-separated list of known filter type names.
BlockFilterType
Definition: blockfilter.h:94
constexpr uint8_t BASIC_FILTER_P
Definition: blockfilter.h:90
constexpr uint32_t BASIC_FILTER_M
Definition: blockfilter.h:91
GCSFilter m_filter
Definition: blockfilter.h:120
const std::vector< unsigned char > & GetEncodedFilter() const LIFETIMEBOUND
Definition: blockfilter.h:139
bool BuildParams(GCSFilter::Params &params) const
uint256 ComputeHeader(const uint256 &prev_header) const
Compute the filter header given the previous one.
BlockFilterType m_filter_type
Definition: blockfilter.h:118
BlockFilter()=default
uint256 GetHash() const
Compute the filter hash.
uint256 m_block_hash
Definition: blockfilter.h:119
Definition: block.h:69
std::vector< CTransactionRef > vtx
Definition: block.h:72
Undo information for a CBlock.
Definition: undo.h:63
std::vector< CTxUndo > vtxundo
Definition: undo.h:65
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:413
SipHash-2-4.
Definition: siphash.h:15
uint64_t Finalize() const
Compute the 64-bit SipHash-2-4 of the data written so far.
Definition: siphash.cpp:81
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:32
An output of a transaction.
Definition: transaction.h:150
CScript scriptPubKey
Definition: transaction.h:153
Undo information for a CTransaction.
Definition: undo.h:53
std::vector< Coin > vprevout
Definition: undo.h:56
A UTXO entry.
Definition: coins.h:33
CTxOut out
unspent transaction output
Definition: coins.h:36
This implements a Golomb-coded set as defined in BIP 158.
Definition: blockfilter.h:30
std::vector< unsigned char > Element
Definition: blockfilter.h:32
uint64_t m_F
Range of element hashes, F = N * M.
Definition: blockfilter.h:50
bool MatchInternal(const uint64_t *sorted_element_hashes, size_t size) const
Helper method used to implement Match and MatchAny.
std::unordered_set< Element, ByteVectorHash > ElementSet
Definition: blockfilter.h:33
uint64_t HashToRange(const Element &element) const
Hash a data element to an integer in the range [0, N * M).
Definition: blockfilter.cpp:26
uint32_t m_N
Number of elements in the filter.
Definition: blockfilter.h:49
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
bool MatchAny(const ElementSet &elements) const
Checks if any of the given elements may be in the set.
std::vector< uint64_t > BuildHashedSet(const ElementSet &elements) const
Definition: blockfilter.cpp:34
Params m_params
Definition: blockfilter.h:48
std::vector< unsigned char > m_encoded
Definition: blockfilter.h:51
Minimal stream for reading from an existing byte array by std::span.
Definition: streams.h:83
constexpr uint64_t GetUint64(int pos) const
Definition: uint256.h:109
256-bit opaque blob.
Definition: uint256.h:196
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
uint64_t GolombRiceDecode(BitStreamReader< IStream > &bitreader, uint8_t P)
Definition: golombrice.h:32
void GolombRiceEncode(BitStreamWriter< OStream > &bitwriter, uint8_t P, uint64_t x)
Definition: golombrice.h:15
uint256 Hash(const T &in1)
Compute the 256-bit hash of an object.
Definition: hash.h:75
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition: string.h:204
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
const char * name
Definition: rest.cpp:50
@ OP_RETURN
Definition: script.h:111
void WriteCompactSize(SizeComputer &os, uint64_t nSize)
Definition: serialize.h:1088
uint64_t ReadCompactSize(Stream &is, bool range_check=true)
Decode a CompactSize-encoded variable-length integer.
Definition: serialize.h:330
uint32_t m_M
Inverse false positive rate.
Definition: blockfilter.h:40
uint64_t m_siphash_k1
Definition: blockfilter.h:38
uint8_t m_P
Golomb-Rice coding parameter.
Definition: blockfilter.h:39
uint64_t m_siphash_k0
Definition: blockfilter.h:37
assert(!tx.IsCoinBase())