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