From 947133dec92cd25ec2b3358c09b8614ba6fb40d4 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 23 Jan 2018 15:59:36 -0800 Subject: [PATCH 01/14] streams: Create VectorReader stream interface for vectors. This is a read analogue for the existing CVectorWriter. --- src/streams.h | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/streams.h b/src/streams.h index 096ebfc9c2e..32a70738155 100644 --- a/src/streams.h +++ b/src/streams.h @@ -139,6 +139,80 @@ private: size_t nPos; }; +/** Minimal stream for reading from an existing vector by reference + */ +class VectorReader +{ +private: + const int m_type; + const int m_version; + const std::vector& m_data; + size_t m_pos = 0; + +public: + + /* + * @param[in] type Serialization Type + * @param[in] version Serialization Version (including any flags) + * @param[in] data Referenced byte vector to overwrite/append + * @param[in] pos Starting position. Vector index where reads should start. + */ + VectorReader(int type, int version, const std::vector& data, size_t pos) + : m_type(type), m_version(version), m_data(data) + { + seek(pos); + } + + /* + * (other params same as above) + * @param[in] args A list of items to deserialize starting at pos. + */ + template + VectorReader(int type, int version, const std::vector& data, size_t pos, + Args&&... args) + : VectorReader(type, version, data, pos) + { + ::UnserializeMany(*this, std::forward(args)...); + } + + template + VectorReader& operator>>(T& obj) + { + // Unserialize from this stream + ::Unserialize(*this, obj); + return (*this); + } + + int GetVersion() const { return m_version; } + int GetType() const { return m_type; } + + size_t size() const { return m_data.size() - m_pos; } + bool empty() const { return m_data.size() == m_pos; } + + void read(char* dst, size_t n) + { + if (n == 0) { + return; + } + + // Read from the beginning of the buffer + size_t pos_next = m_pos + n; + if (pos_next > m_data.size()) { + throw std::ios_base::failure("VectorReader::read(): end of data"); + } + memcpy(dst, m_data.data() + m_pos, n); + m_pos = pos_next; + } + + void seek(size_t n) + { + m_pos += n; + if (m_pos > m_data.size()) { + throw std::ios_base::failure("VectorReader::seek(): end of data"); + } + } +}; + /** Double ended buffer combining vector and stream-like interfaces. * * >> and << read and write unformatted data using the above serialization templates. From 87f2d9ee43a9220076b1959d1ca65245d9591be9 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Wed, 11 Apr 2018 20:51:44 -0700 Subject: [PATCH 02/14] streams: Unit test for VectorReader class. --- src/test/streams_tests.cpp | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 6a0aa864d9c..592ebcbd0a0 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -68,6 +68,51 @@ BOOST_AUTO_TEST_CASE(streams_vector_writer) vch.clear(); } +BOOST_AUTO_TEST_CASE(streams_vector_reader) +{ + std::vector vch = {1, 255, 3, 4, 5, 6}; + + VectorReader reader(SER_NETWORK, INIT_PROTO_VERSION, vch, 0); + BOOST_CHECK_EQUAL(reader.size(), 6); + BOOST_CHECK(!reader.empty()); + + // Read a single byte as an unsigned char. + unsigned char a; + reader >> a; + BOOST_CHECK_EQUAL(a, 1); + BOOST_CHECK_EQUAL(reader.size(), 5); + BOOST_CHECK(!reader.empty()); + + // Read a single byte as a signed char. + signed char b; + reader >> b; + BOOST_CHECK_EQUAL(b, -1); + BOOST_CHECK_EQUAL(reader.size(), 4); + BOOST_CHECK(!reader.empty()); + + // Read a 4 bytes as an unsigned int. + unsigned int c; + reader >> c; + BOOST_CHECK_EQUAL(c, 100992003); // 3,4,5,6 in little-endian base-256 + BOOST_CHECK_EQUAL(reader.size(), 0); + BOOST_CHECK(reader.empty()); + + // Reading after end of byte vector throws an error. + signed int d; + BOOST_CHECK_THROW(reader >> d, std::ios_base::failure); + + // Read a 4 bytes as a signed int from the beginning of the buffer. + reader.seek(-6); + reader >> d; + BOOST_CHECK_EQUAL(d, 67370753); // 1,255,3,4 in little-endian base-256 + BOOST_CHECK_EQUAL(reader.size(), 2); + BOOST_CHECK(!reader.empty()); + + // Reading after end of byte vector throws an error even if the reader is + // not totally empty. + BOOST_CHECK_THROW(reader >> d, std::ios_base::failure); +} + BOOST_AUTO_TEST_CASE(streams_serializedata_xor) { std::vector in; From fe943f99bf0a2bbb12e30bc4803c0337e3c95b93 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 23 Jan 2018 16:12:51 -0800 Subject: [PATCH 03/14] streams: Implement BitStreamReader/Writer classes. Golomb-Rice coding, as specified in BIP 158, involves operations on individual bits. These classes will be used to implement the encoding/decoding operations. --- src/streams.h | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/streams.h b/src/streams.h index 32a70738155..d4a309be3cd 100644 --- a/src/streams.h +++ b/src/streams.h @@ -510,12 +510,105 @@ public: } }; +template +class BitStreamReader +{ +private: + IStream& m_istream; + /// Buffered byte read in from the input stream. A new byte is read into the + /// buffer when m_offset reaches 8. + uint8_t m_buffer{0}; + /// Number of high order bits in m_buffer already returned by previous + /// Read() calls. The next bit to be returned is at this offset from the + /// most significant bit position. + int m_offset{8}; +public: + explicit BitStreamReader(IStream& istream) : m_istream(istream) {} + /** Read the specified number of bits from the stream. The data is returned + * in the nbits least signficant bits of a 64-bit uint. + */ + uint64_t Read(int nbits) { + if (nbits < 0 || nbits > 64) { + throw std::out_of_range("nbits must be between 0 and 64"); + } + uint64_t data = 0; + while (nbits > 0) { + if (m_offset == 8) { + m_istream >> m_buffer; + m_offset = 0; + } + int bits = std::min(8 - m_offset, nbits); + data <<= bits; + data |= static_cast(m_buffer << m_offset) >> (8 - bits); + m_offset += bits; + nbits -= bits; + } + return data; + } +}; + +template +class BitStreamWriter +{ +private: + OStream& m_ostream; + + /// Buffered byte waiting to be written to the output stream. The byte is + /// written buffer when m_offset reaches 8 or Flush() is called. + uint8_t m_buffer{0}; + + /// Number of high order bits in m_buffer already written by previous + /// Write() calls and not yet flushed to the stream. The next bit to be + /// written to is at this offset from the most significant bit position. + int m_offset{0}; + +public: + explicit BitStreamWriter(OStream& ostream) : m_ostream(ostream) {} + + ~BitStreamWriter() + { + Flush(); + } + + /** Write the nbits least significant bits of a 64-bit int to the output + * stream. Data is buffered until it completes an octet. + */ + void Write(uint64_t data, int nbits) { + if (nbits < 0 || nbits > 64) { + throw std::out_of_range("nbits must be between 0 and 64"); + } + + while (nbits > 0) { + int bits = std::min(8 - m_offset, nbits); + m_buffer |= (data << (64 - nbits)) >> (64 - 8 + m_offset); + m_offset += bits; + nbits -= bits; + + if (m_offset == 8) { + Flush(); + } + } + } + + /** Flush any unwritten bits to the output stream, padding with 0's to the + * next byte boundary. + */ + void Flush() { + if (m_offset == 0) { + return; + } + + m_ostream << m_buffer; + m_buffer = 0; + m_offset = 0; + } +}; From 9b622dc72279b027c59d6541cddff53800fc689b Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 17 Apr 2018 02:19:36 -0700 Subject: [PATCH 04/14] streams: Unit tests for BitStreamReader and BitStreamWriter. --- src/test/streams_tests.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 592ebcbd0a0..26cf74830da 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -113,6 +113,41 @@ BOOST_AUTO_TEST_CASE(streams_vector_reader) BOOST_CHECK_THROW(reader >> d, std::ios_base::failure); } +BOOST_AUTO_TEST_CASE(bitstream_reader_writer) +{ + CDataStream data(SER_NETWORK, INIT_PROTO_VERSION); + + BitStreamWriter bit_writer(data); + bit_writer.Write(0, 1); + bit_writer.Write(2, 2); + bit_writer.Write(6, 3); + bit_writer.Write(11, 4); + bit_writer.Write(1, 5); + bit_writer.Write(32, 6); + bit_writer.Write(7, 7); + bit_writer.Write(30497, 16); + bit_writer.Flush(); + + CDataStream data_copy(data); + uint32_t serialized_int1; + data >> serialized_int1; + BOOST_CHECK_EQUAL(serialized_int1, (uint32_t)0x7700C35A); // NOTE: Serialized as LE + uint16_t serialized_int2; + data >> serialized_int2; + BOOST_CHECK_EQUAL(serialized_int2, (uint16_t)0x1072); // NOTE: Serialized as LE + + BitStreamReader bit_reader(data_copy); + BOOST_CHECK_EQUAL(bit_reader.Read(1), 0); + BOOST_CHECK_EQUAL(bit_reader.Read(2), 2); + BOOST_CHECK_EQUAL(bit_reader.Read(3), 6); + BOOST_CHECK_EQUAL(bit_reader.Read(4), 11); + BOOST_CHECK_EQUAL(bit_reader.Read(5), 1); + BOOST_CHECK_EQUAL(bit_reader.Read(6), 32); + BOOST_CHECK_EQUAL(bit_reader.Read(7), 7); + BOOST_CHECK_EQUAL(bit_reader.Read(16), 30497); + BOOST_CHECK_THROW(bit_reader.Read(8), std::ios_base::failure); +} + BOOST_AUTO_TEST_CASE(streams_serializedata_xor) { std::vector in; From c454f0ac63c6028f54c7eb51683b3ccdb475b19b Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 23 Jan 2018 16:10:08 -0800 Subject: [PATCH 05/14] blockfilter: Declare GCSFilter class for BIP 158 impl. --- src/Makefile.am | 1 + src/blockfilter.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/blockfilter.h diff --git a/src/Makefile.am b/src/Makefile.am index d1693fa85cc..d8f8e57b4d9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -96,6 +96,7 @@ BITCOIN_CORE_H = \ bech32.h \ bloom.h \ blockencodings.h \ + blockfilter.h \ chain.h \ chainparams.h \ chainparamsbase.h \ diff --git a/src/blockfilter.h b/src/blockfilter.h new file mode 100644 index 00000000000..c0bd69b3326 --- /dev/null +++ b/src/blockfilter.h @@ -0,0 +1,66 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_BLOCKFILTER_H +#define BITCOIN_BLOCKFILTER_H + +#include +#include +#include + +#include +#include + +/** + * This implements a Golomb-coded set as defined in BIP 158. It is a + * compact, probabilistic data structure for testing set membership. + */ +class GCSFilter +{ +public: + typedef std::vector Element; + typedef std::set ElementSet; + +private: + uint64_t m_siphash_k0; + uint64_t m_siphash_k1; + uint8_t m_P; //!< Golomb-Rice coding parameter + uint32_t m_M; //!< Inverse false positive rate + uint32_t m_N; //!< Number of elements in the filter + uint64_t m_F; //!< Range of element hashes, F = N * M + std::vector m_encoded; + +public: + + /** Constructs an empty filter. */ + GCSFilter(uint64_t siphash_k0 = 0, uint64_t siphash_k1 = 0, uint8_t P = 0, uint32_t M = 0); + + /** Reconstructs an already-created filter from an encoding. */ + GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, + std::vector encoded_filter); + + /** Builds a new filter from the params and set of elements. */ + GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, + const ElementSet& elements); + + uint8_t GetP() const { return m_P; } + uint32_t GetN() const { return m_N; } + uint32_t GetM() const { return m_M; } + const std::vector& GetEncoded() const { return m_encoded; } + + /** + * Checks if the element may be in the set. False positives are possible + * with probability 1/M. + */ + bool Match(const Element& element) const; + + /** + * Checks if any of the given elements may be in the set. False positives + * are possible with probability 1/M per element checked. This is more + * efficient that checking Match on multiple elements separately. + */ + bool MatchAny(const ElementSet& elements) const; +}; + +#endif // BITCOIN_BLOCKFILTER_H From cf70b550054eed36f194eaa13f4a9cb31e32df38 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 23 Jan 2018 16:25:21 -0800 Subject: [PATCH 06/14] blockfilter: Implement GCSFilter constructors. --- src/Makefile.am | 1 + src/blockfilter.cpp | 151 ++++++++++++++++++++++++++++++++++++++++++++ src/blockfilter.h | 5 ++ 3 files changed, 157 insertions(+) create mode 100644 src/blockfilter.cpp diff --git a/src/Makefile.am b/src/Makefile.am index d8f8e57b4d9..3701ee8f3c7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -220,6 +220,7 @@ libbitcoin_server_a_SOURCES = \ addrman.cpp \ bloom.cpp \ blockencodings.cpp \ + blockfilter.cpp \ chain.cpp \ checkpoints.cpp \ consensus/tx_verify.cpp \ diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp new file mode 100644 index 00000000000..e4c95ccfbd1 --- /dev/null +++ b/src/blockfilter.cpp @@ -0,0 +1,151 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include + +/// SerType used to serialize parameters in GCS filter encoding. +static constexpr int GCS_SER_TYPE = SER_NETWORK; + +/// Protocol version used to serialize parameters in GCS filter encoding. +static constexpr int GCS_SER_VERSION = 0; + +template +static void GolombRiceEncode(BitStreamWriter& bitwriter, uint8_t P, uint64_t x) +{ + // Write quotient as unary-encoded: q 1's followed by one 0. + uint64_t q = x >> P; + while (q > 0) { + int nbits = q <= 64 ? static_cast(q) : 64; + bitwriter.Write(~0ULL, nbits); + q -= nbits; + } + bitwriter.Write(0, 1); + + // Write the remainder in P bits. Since the remainder is just the bottom + // P bits of x, there is no need to mask first. + bitwriter.Write(x, P); +} + +template +static uint64_t GolombRiceDecode(BitStreamReader& bitreader, uint8_t P) +{ + // Read unary-encoded quotient: q 1's followed by one 0. + uint64_t q = 0; + while (bitreader.Read(1) == 1) { + ++q; + } + + uint64_t r = bitreader.Read(P); + + return (q << P) + r; +} + +// Map a value x that is uniformly distributed in the range [0, 2^64) to a +// value uniformly distributed in [0, n) by returning the upper 64 bits of +// x * n. +// +// See: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ +static uint64_t MapIntoRange(uint64_t x, uint64_t n) +{ + // To perform the calculation on 64-bit numbers without losing the + // result to overflow, split the numbers into the most significant and + // least significant 32 bits and perform multiplication piece-wise. + // + // See: https://stackoverflow.com/a/26855440 + uint64_t x_hi = x >> 32; + uint64_t x_lo = x & 0xFFFFFFFF; + uint64_t n_hi = n >> 32; + uint64_t n_lo = n & 0xFFFFFFFF; + + uint64_t ac = x_hi * n_hi; + uint64_t ad = x_hi * n_lo; + uint64_t bc = x_lo * n_hi; + uint64_t bd = x_lo * n_lo; + + uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF); + uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32); + return upper64; +} + +uint64_t GCSFilter::HashToRange(const Element& element) const +{ + uint64_t hash = CSipHasher(m_siphash_k0, m_siphash_k1) + .Write(element.data(), element.size()) + .Finalize(); + return MapIntoRange(hash, m_F); +} + +std::vector GCSFilter::BuildHashedSet(const ElementSet& elements) const +{ + std::vector hashed_elements; + hashed_elements.reserve(elements.size()); + for (const Element& element : elements) { + hashed_elements.push_back(HashToRange(element)); + } + std::sort(hashed_elements.begin(), hashed_elements.end()); + return hashed_elements; +} + +GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M) + : m_siphash_k0(siphash_k0), m_siphash_k1(siphash_k1), m_P(P), m_M(M), m_N(0), m_F(0) +{} + +GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, + std::vector encoded_filter) + : GCSFilter(siphash_k0, siphash_k1, P, M) +{ + m_encoded = std::move(encoded_filter); + + VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); + + uint64_t N = ReadCompactSize(stream); + m_N = static_cast(N); + if (m_N != N) { + throw std::ios_base::failure("N must be <2^32"); + } + m_F = static_cast(m_N) * static_cast(m_M); + + // Verify that the encoded filter contains exactly N elements. If it has too much or too little + // data, a std::ios_base::failure exception will be raised. + BitStreamReader bitreader(stream); + for (uint64_t i = 0; i < m_N; ++i) { + GolombRiceDecode(bitreader, m_P); + } + if (!stream.empty()) { + throw std::ios_base::failure("encoded_filter contains excess data"); + } +} + +GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, + const ElementSet& elements) + : GCSFilter(siphash_k0, siphash_k1, P, M) +{ + size_t N = elements.size(); + m_N = static_cast(N); + if (m_N != N) { + throw std::invalid_argument("N must be <2^32"); + } + m_F = static_cast(m_N) * static_cast(m_M); + + CVectorWriter stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); + + WriteCompactSize(stream, m_N); + + if (elements.empty()) { + return; + } + + BitStreamWriter bitwriter(stream); + + uint64_t last_value = 0; + for (uint64_t value : BuildHashedSet(elements)) { + uint64_t delta = value - last_value; + GolombRiceEncode(bitwriter, m_P, delta); + last_value = value; + } + + bitwriter.Flush(); +} diff --git a/src/blockfilter.h b/src/blockfilter.h index c0bd69b3326..7809e6875ab 100644 --- a/src/blockfilter.h +++ b/src/blockfilter.h @@ -31,6 +31,11 @@ private: uint64_t m_F; //!< Range of element hashes, F = N * M std::vector m_encoded; + /** Hash a data element to an integer in the range [0, N * M). */ + uint64_t HashToRange(const Element& element) const; + + std::vector BuildHashedSet(const ElementSet& elements) const; + public: /** Constructs an empty filter. */ From 558c536e35a25594881693e6ff01d275c88d7af1 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 23 Jan 2018 16:33:26 -0800 Subject: [PATCH 07/14] blockfilter: Implement GCSFilter Match methods. --- src/blockfilter.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/blockfilter.h | 3 +++ 2 files changed, 47 insertions(+) diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp index e4c95ccfbd1..52d8f8c2965 100644 --- a/src/blockfilter.cpp +++ b/src/blockfilter.cpp @@ -149,3 +149,47 @@ GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32 bitwriter.Flush(); } + +bool GCSFilter::MatchInternal(const uint64_t* element_hashes, size_t size) const +{ + VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); + + // Seek forward by size of N + uint64_t N = ReadCompactSize(stream); + assert(N == m_N); + + BitStreamReader bitreader(stream); + + uint64_t value = 0; + size_t hashes_index = 0; + for (uint32_t i = 0; i < m_N; ++i) { + uint64_t delta = GolombRiceDecode(bitreader, m_P); + value += delta; + + while (true) { + if (hashes_index == size) { + return false; + } else if (element_hashes[hashes_index] == value) { + return true; + } else if (element_hashes[hashes_index] > value) { + break; + } + + hashes_index++; + } + } + + return false; +} + +bool GCSFilter::Match(const Element& element) const +{ + uint64_t query = HashToRange(element); + return MatchInternal(&query, 1); +} + +bool GCSFilter::MatchAny(const ElementSet& elements) const +{ + const std::vector queries = BuildHashedSet(elements); + return MatchInternal(queries.data(), queries.size()); +} diff --git a/src/blockfilter.h b/src/blockfilter.h index 7809e6875ab..d45d31046f9 100644 --- a/src/blockfilter.h +++ b/src/blockfilter.h @@ -36,6 +36,9 @@ private: std::vector BuildHashedSet(const ElementSet& elements) const; + /** Helper method used to implement Match and MatchAny */ + bool MatchInternal(const uint64_t* sorted_element_hashes, size_t size) const; + public: /** Constructs an empty filter. */ From 53e7874e079f9ddfe8b176f11d46e6b59c7283d5 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 23 Jan 2018 17:13:04 -0800 Subject: [PATCH 08/14] blockfilter: Simple test for GCSFilter construction and Match. --- src/Makefile.test.include | 1 + src/test/blockfilter_tests.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/test/blockfilter_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 6f401636f52..1e318eed021 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -39,6 +39,7 @@ BITCOIN_TESTS =\ test/bip32_tests.cpp \ test/blockchain_tests.cpp \ test/blockencodings_tests.cpp \ + test/blockfilter_tests.cpp \ test/bloom_tests.cpp \ test/bswap_tests.cpp \ test/checkqueue_tests.cpp \ diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp new file mode 100644 index 00000000000..339a7fcfeba --- /dev/null +++ b/src/test/blockfilter_tests.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include + +BOOST_AUTO_TEST_SUITE(blockfilter_tests) + +BOOST_AUTO_TEST_CASE(gcsfilter_test) +{ + GCSFilter::ElementSet included_elements, excluded_elements; + for (int i = 0; i < 100; ++i) { + GCSFilter::Element element1(32); + element1[0] = i; + included_elements.insert(std::move(element1)); + + GCSFilter::Element element2(32); + element2[1] = i; + excluded_elements.insert(std::move(element2)); + } + + GCSFilter filter(0, 0, 10, 1 << 10, included_elements); + for (const auto& element : included_elements) { + BOOST_CHECK(filter.Match(element)); + + auto insertion = excluded_elements.insert(element); + BOOST_CHECK(filter.MatchAny(excluded_elements)); + excluded_elements.erase(insertion.first); + } +} + +BOOST_AUTO_TEST_SUITE_END() From c1855f6052aca806fdb51be01b30dfeee8b55f40 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Tue, 23 Jan 2018 17:25:30 -0800 Subject: [PATCH 09/14] blockfilter: Construction of basic block filters. --- src/blockfilter.cpp | 40 +++++++++++++++++++++++++ src/blockfilter.h | 35 ++++++++++++++++++++++ src/test/blockfilter_tests.cpp | 53 ++++++++++++++++++++++++++++++++++ src/undo.h | 1 + 4 files changed, 129 insertions(+) diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp index 52d8f8c2965..1119b549015 100644 --- a/src/blockfilter.cpp +++ b/src/blockfilter.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include