22// Distributed under the MIT software license, see the accompanying
33// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44
5+ #include < cassert>
6+ #include < cmath>
7+ #include < csignal>
8+ #include < cstdint>
59#include < mutex>
10+ #include < queue>
611#include < set>
12+ #include < stack>
713#include < string_view>
814
915#include < blockfilter.h>
@@ -21,6 +27,7 @@ using util::Join;
2127
2228static const std::map<BlockFilterType, std::string> g_filter_types = {
2329 {BlockFilterType::BASIC, " basic" },
30+ {BlockFilterType::FUSE, " fuse" },
2431};
2532
2633uint64_t GCSFilter::HashToRange (const Element& element) const
@@ -145,6 +152,115 @@ bool GCSFilter::MatchAny(const ElementSet& elements) const
145152 return MatchInternal (queries.data (), queries.size ());
146153}
147154
155+ uint64_t BinaryFuseFilter::Hash (const BinaryFuseFilter::Element& element) const
156+ {
157+ return CSipHasher (m_siphash_k0, m_siphash_k1)
158+ .Write (element)
159+ .Finalize ();
160+ }
161+
162+ uint64_t BinaryFuseFilter::Mix (uint64_t key) const {
163+ return CSipHasher (m_siphash_k0, m_siphash_k1)
164+ .Write (key)
165+ .Finalize ();
166+ }
167+
168+ uint16_t BinaryFuseFilter::Fingerprint (uint64_t key) const
169+ {
170+ return static_cast <uint16_t >(key) ^ key >> 48 ;
171+ }
172+
173+ std::tuple<uint32_t , uint32_t , uint32_t > BinaryFuseFilter::Slots (const uint64_t key) const
174+ {
175+ uint32_t start_seg = key % (m_num_segments - m_arity + 1 );
176+ const auto h0_entropy = Mix (key);
177+ const auto h1_entropy = Mix (h0_entropy);
178+ const auto h2_entropy = Mix (h1_entropy);
179+ uint32_t h0 = static_cast <uint32_t >((start_seg + 0 ) * m_segment_len + FastRange32 (h0_entropy, m_segment_len));
180+ uint32_t h1 = static_cast <uint32_t >((start_seg + 1 ) * m_segment_len + FastRange32 (h1_entropy, m_segment_len));
181+ uint32_t h2 = static_cast <uint32_t >((start_seg + 2 ) * m_segment_len + FastRange32 (h2_entropy, m_segment_len));
182+ return {h0, h1, h2};
183+ }
184+
185+ bool BinaryFuseFilter::Query (const Element& element) const
186+ {
187+ const auto key = Hash (element);
188+ const auto [h0, h1, h2] = Slots (key);
189+ const auto f = Fingerprint (key);
190+ return f == (m_fingerprints[h0] ^ m_fingerprints[h1] ^ m_fingerprints[h2]);
191+ }
192+
193+ BinaryFuseFilter::BinaryFuseFilter (const ElementSet& elements, const uint256& hash) {
194+ m_siphash_k0 = hash.GetUint64 (0 );
195+ m_siphash_k1 = hash.GetUint64 (1 );
196+ const uint32_t n = static_cast <uint32_t >(elements.size ());
197+ const auto exp = static_cast <uint32_t >(std::floor ((std::log (static_cast <double >(n)) / std::log (3.33 ) + 2.25 )));
198+ m_segment_len = std::max (uint32_t {2 }, uint32_t {1 } << exp);
199+ auto target_array_len = static_cast <uint32_t >(std::ceil (n * 1.125 ));
200+ m_num_segments = std::max (CeilDiv (target_array_len, m_segment_len), m_arity);
201+ auto array_len = m_num_segments * m_segment_len;
202+ m_fingerprints.assign (array_len, 0 );
203+ std::vector degrees = std::vector<Degree>(array_len);
204+ degrees.assign (array_len, Degree ());
205+ for (const auto & element : elements) {
206+ const auto key = Hash (element);
207+ auto [h0, h1, h2] = Slots (key);
208+ degrees[h0].m_degree ++; degrees[h0].m_xor ^= key;
209+ degrees[h1].m_degree ++; degrees[h1].m_xor ^= key;
210+ degrees[h2].m_degree ++; degrees[h2].m_xor ^= key;
211+ }
212+ std::queue q = std::queue<uint32_t >{};
213+ for (uint32_t i{0 }; i < array_len; ++i) {
214+ if (degrees[i].m_degree == 1 ) {
215+ q.push (i);
216+ }
217+ }
218+ std::stack p = std::stack<Assignment>{};
219+ while (!q.empty ()) {
220+ const auto index = q.front ();
221+ q.pop ();
222+ if (degrees[index].m_degree != 1 ) continue ;
223+ uint64_t hash = degrees[index].m_xor ;
224+ p.emplace (index, hash);
225+ const auto [h0, h1, h2] = Slots (hash);
226+ degrees[h0].m_degree --;
227+ degrees[h1].m_degree --;
228+ degrees[h2].m_degree --;
229+ degrees[h0].m_xor ^= hash;
230+ degrees[h1].m_xor ^= hash;
231+ degrees[h2].m_xor ^= hash;
232+ if (degrees[h0].m_degree == 1 ) q.push (h0);
233+ if (degrees[h1].m_degree == 1 ) q.push (h1);
234+ if (degrees[h2].m_degree == 1 ) q.push (h2);
235+ }
236+ // check P is size N
237+ // assert(p.size() == n);
238+ while (!p.empty ()) {
239+ const auto assignment = p.top ();
240+ p.pop ();
241+ const auto hash = assignment.m_hash ;
242+ const auto f = Fingerprint (hash);
243+ const auto [h0, h1, h2] = Slots (hash);
244+ const auto i = assignment.m_index ;
245+ m_fingerprints[i] = f ^ m_fingerprints[h0] ^ m_fingerprints[h1] ^ m_fingerprints[h2];
246+ }
247+ DataStream writer{m_encoded};
248+ this ->Serialize (writer);
249+ }
250+
251+ bool BinaryFuseFilter::MatchAny (const ElementSet& elements) const {
252+ for (const auto & element : elements) {
253+ if (Match (element)) {
254+ return true ;
255+ }
256+ }
257+ return false ;
258+ }
259+
260+ bool BinaryFuseFilter::Match (const Element& element) const {
261+ return Query (element);
262+ }
263+
148264const std::string& BlockFilterTypeName (BlockFilterType filter_type)
149265{
150266 static std::string unknown_retval;
@@ -226,7 +342,12 @@ BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const
226342 if (!BuildParams (params)) {
227343 throw std::invalid_argument (" unknown filter_type" );
228344 }
229- m_filter = std::make_unique<GCSFilter>(params, BasicFilterElements (block, block_undo));
345+ if (filter_type == BlockFilterType::BASIC) {
346+ m_filter = std::make_unique<GCSFilter>(params, BasicFilterElements (block, block_undo));
347+ }
348+ if (filter_type == BlockFilterType::FUSE) {
349+ m_filter = std::make_unique<BinaryFuseFilter>(BinaryFuseFilter::build (BasicFilterElements (block, block_undo), m_block_hash));
350+ }
230351}
231352
232353bool BlockFilter::BuildParams (GCSFilter::Params& params) const
@@ -238,6 +359,7 @@ bool BlockFilter::BuildParams(GCSFilter::Params& params) const
238359 params.m_P = BASIC_FILTER_P;
239360 params.m_M = BASIC_FILTER_M;
240361 return true ;
362+ case BlockFilterType::FUSE: return true ;
241363 case BlockFilterType::INVALID:
242364 return false ;
243365 }
0 commit comments