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