From 451c583023704202ef2d6524808dd0b818e46615 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Fri, 24 Dec 2010 01:31:41 +0000 Subject: [PATCH] more robust mechanism to determine external IP --- CMakeLists.txt | 1 + Jamfile | 1 + include/libtorrent/Makefile.am | 1 + include/libtorrent/aux_/session_impl.hpp | 40 ++++++++- include/libtorrent/bloom_filter.hpp | 72 ++++++++++++++++ include/libtorrent/debug.hpp | 6 ++ include/libtorrent/socket_io.hpp | 2 + src/Makefile.am | 1 + src/bloom_filter.cpp | 43 ++++++++++ src/bt_peer_connection.cpp | 9 +- src/kademlia/node_id.cpp | 17 +--- src/kademlia/rpc_manager.cpp | 4 +- src/session_impl.cpp | 102 +++++++++++++++++++---- src/socket_io.cpp | 17 ++++ src/torrent.cpp | 4 +- test/test_primitives.cpp | 79 ++++++++++++++++++ 16 files changed, 360 insertions(+), 39 deletions(-) create mode 100644 include/libtorrent/bloom_filter.hpp create mode 100644 src/bloom_filter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 605f63e18..f4a1ff62e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(sources bandwidth_limit bandwidth_manager bandwidth_queue_entry + bloom_filter connection_queue create_torrent disk_buffer_holder diff --git a/Jamfile b/Jamfile index f0199d16f..5f9597206 100755 --- a/Jamfile +++ b/Jamfile @@ -368,6 +368,7 @@ SOURCES = bandwidth_limit bandwidth_manager bandwidth_queue_entry + bloom_filter connection_queue create_torrent disk_buffer_holder diff --git a/include/libtorrent/Makefile.am b/include/libtorrent/Makefile.am index 9bf0fac2f..e518e1063 100644 --- a/include/libtorrent/Makefile.am +++ b/include/libtorrent/Makefile.am @@ -18,6 +18,7 @@ nobase_include_HEADERS = \ bandwidth_queue_entry.hpp \ bencode.hpp \ bitfield.hpp \ + bloom_filter.hpp \ broadcast_socket.hpp \ bt_peer_connection.hpp \ buffer.hpp \ diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 230720750..9c55a960d 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -83,6 +83,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/socket_io.hpp" // for print_address #include "libtorrent/address.hpp" #include "libtorrent/utp_socket_manager.hpp" +#include "libtorrent/bloom_filter.hpp" #ifdef TORRENT_STATS #include @@ -393,7 +394,16 @@ namespace libtorrent char* allocate_disk_buffer(char const* category); void free_disk_buffer(char* buf); - void set_external_address(address const& ip); + enum + { + source_dht = 1, + source_peer = 2, + source_tracker = 4, + source_router = 8 + }; + + void set_external_address(address const& ip + , int source_type, address const& source); address const& external_address() const { return m_external_address; } // used when posting synchronous function @@ -834,6 +844,34 @@ namespace libtorrent #ifdef TORRENT_UPNP_LOGGING std::ofstream m_upnp_log; #endif + struct external_ip_t + { + external_ip_t(): sources(0), num_votes(0) {} + + bool add_vote(sha1_hash const& k, int type); + bool operator<(external_ip_t const& rhs) const + { + if (num_votes < rhs.num_votes) return true; + if (rhs.num_votes > num_votes) return false; + return sources < rhs.sources; + } + + // this is a bloom filter of the IPs that have + // reported this address + bloom_filter<16> voters; + // this is the actual external address + address addr; + // a bitmask of sources the reporters have come from + boost::uint16_t sources; + // the total number of votes for this IP + boost::uint16_t num_votes; + }; + + // this is a bloom filter of all the IPs that have + // been the first to report an external address. Each + // IP only gets to add a new item once. + bloom_filter<32> m_external_address_voters; + std::vector m_external_addresses; address m_external_address; #ifndef TORRENT_DISABLE_EXTENSIONS diff --git a/include/libtorrent/bloom_filter.hpp b/include/libtorrent/bloom_filter.hpp new file mode 100644 index 000000000..f180c0b5e --- /dev/null +++ b/include/libtorrent/bloom_filter.hpp @@ -0,0 +1,72 @@ +/* + +Copyright (c) 2010, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BLOOM_FILTER_HPP_INCLUDED +#define TORRENT_BLOOM_FILTER_HPP_INCLUDED + +#include +#include "libtorrent/peer_id.hpp" // for sha1_hash + +namespace libtorrent +{ + void set_bit(boost::uint32_t b, boost::uint8_t* bits, int len); + bool has_bit(boost::uint32_t b, boost::uint8_t const* bits, int len); + + template + struct bloom_filter + { + bool find(sha1_hash const& k) const + { + return has_bit(k[0], bits, N) + && has_bit(k[1], bits, N) + && has_bit(k[2], bits, N); + } + + void set(sha1_hash const& k) + { + set_bit(k[0], bits, N); + set_bit(k[1], bits, N); + set_bit(k[2], bits, N); + } + + void clear() { memset(bits, 0, N); } + + bloom_filter() { clear(); } + + private: + boost::uint8_t bits[N]; + }; + +} + +#endif + diff --git a/include/libtorrent/debug.hpp b/include/libtorrent/debug.hpp index edc5f5033..39b14a605 100644 --- a/include/libtorrent/debug.hpp +++ b/include/libtorrent/debug.hpp @@ -124,6 +124,12 @@ namespace libtorrent static mutex file_mutex; #endif + ~logger() + { + mutex::scoped_lock l(file_mutex); + log_file.close(); + } + logger(std::string const& logpath, std::string const& filename , int instance, bool append) { diff --git a/include/libtorrent/socket_io.hpp b/include/libtorrent/socket_io.hpp index a830f8c31..73e78cd1f 100644 --- a/include/libtorrent/socket_io.hpp +++ b/include/libtorrent/socket_io.hpp @@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/io.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/lazy_entry.hpp" +#include "libtorrent/peer_id.hpp" // for sha1_hash #include namespace libtorrent @@ -46,6 +47,7 @@ namespace libtorrent TORRENT_EXPORT std::string print_endpoint(tcp::endpoint const& ep); TORRENT_EXPORT std::string print_endpoint(udp::endpoint const& ep); TORRENT_EXPORT std::string address_to_bytes(address const& a); + TORRENT_EXPORT void hash_address(address const& ip, sha1_hash& h); namespace detail { diff --git a/src/Makefile.am b/src/Makefile.am index f8c27a191..7616e743f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,6 +24,7 @@ libtorrent_rasterbar_la_SOURCES = \ bandwidth_limit.cpp \ bandwidth_manager.cpp \ bandwidth_queue_entry.cpp \ + bloom_filter.cpp \ broadcast_socket.cpp \ bt_peer_connection.cpp \ connection_queue.cpp \ diff --git a/src/bloom_filter.cpp b/src/bloom_filter.cpp new file mode 100644 index 000000000..d6418c152 --- /dev/null +++ b/src/bloom_filter.cpp @@ -0,0 +1,43 @@ +/* + +Copyright (c) 2010, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/bloom_filter.hpp" + +namespace libtorrent +{ + bool has_bit(boost::uint32_t b, boost::uint8_t const* bits, int len) + { b %= len * 8; return bits[b/8] & (1 << (b & 7)); } + + void set_bit(boost::uint32_t b, boost::uint8_t* bits, int len) + { b %= len * 8; bits[b/8] |= (1 << (b & 7)); } +} + diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index 7e1041b92..d4fcdc2d8 100644 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -1788,7 +1788,8 @@ namespace libtorrent { address_v4::bytes_type bytes; std::copy(myip.begin(), myip.end(), bytes.begin()); - m_ses.set_external_address(address_v4(bytes)); + m_ses.set_external_address(address_v4(bytes) + , aux::session_impl::source_peer, remote().address()); } #if TORRENT_USE_IPV6 else if (myip.size() == address_v6::bytes_type::static_size) @@ -1797,9 +1798,11 @@ namespace libtorrent std::copy(myip.begin(), myip.end(), bytes.begin()); address_v6 ipv6_address(bytes); if (ipv6_address.is_v4_mapped()) - m_ses.set_external_address(ipv6_address.to_v4()); + m_ses.set_external_address(ipv6_address.to_v4() + , aux::session_impl::source_peer, remote().address()); else - m_ses.set_external_address(ipv6_address); + m_ses.set_external_address(ipv6_address + , aux::session_impl::source_peer, remote().address()); } #endif } diff --git a/src/kademlia/node_id.cpp b/src/kademlia/node_id.cpp index 9b85a2824..d19729338 100644 --- a/src/kademlia/node_id.cpp +++ b/src/kademlia/node_id.cpp @@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/hasher.hpp" #include "libtorrent/assert.hpp" #include "libtorrent/broadcast_socket.hpp" // for is_local et.al +#include "libtorrent/socket_io.hpp" // for hash_address namespace libtorrent { namespace dht { @@ -96,22 +97,6 @@ int distance_exp(node_id const& n1, node_id const& n2) struct static_ { static_() { std::srand(std::time(0)); } } static__; -void hash_address(address const& ip, sha1_hash& h) -{ -#if TORRENT_USE_IPV6 - if (ip.is_v6()) - { - address_v6::bytes_type b = ip.to_v6().to_bytes(); - h = hasher((char*)&b[0], b.size()).final(); - } - else -#endif - { - address_v4::bytes_type b = ip.to_v4().to_bytes(); - h = hasher((char*)&b[0], b.size()).final(); - } -} - // verifies whether a node-id matches the IP it's used from // returns true if the node-id is OK coming from this source // and false otherwise. diff --git a/src/kademlia/rpc_manager.cpp b/src/kademlia/rpc_manager.cpp index fee63dcc4..f7b279fbf 100644 --- a/src/kademlia/rpc_manager.cpp +++ b/src/kademlia/rpc_manager.cpp @@ -346,7 +346,7 @@ bool rpc_manager::incoming(msg const& m, node_id* id) // this node claims we use the wrong node-ID! address_v4::bytes_type b; memcpy(&b[0], ext_ip->string_ptr(), 4); - m_ses.set_external_address(address_v4(b)); + m_ses.set_external_address(address_v4(b), aux::session_impl::source_dht, m.addr.address()); } #if TORRENT_USE_IPV6 else if (ext_ip && ext_ip->string_length() == 16) @@ -354,7 +354,7 @@ bool rpc_manager::incoming(msg const& m, node_id* id) // this node claims we use the wrong node-ID! address_v6::bytes_type b; memcpy(&b[0], ext_ip->string_ptr(), 16); - m_ses.set_external_address(address_v6(b)); + m_ses.set_external_address(address_v6(b), aux::session_impl::source_dht, m.addr.address()); } #endif diff --git a/src/session_impl.cpp b/src/session_impl.cpp index dee7de525..1f018ab80 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -900,11 +900,6 @@ namespace aux { save_struct(e[c.name], reinterpret_cast(this) + c.offset , c.map, c.num_entries, reinterpret_cast(&def) + c.default_offset); } -#ifndef TORRENT_DISABLE_DHT - if (flags & session::save_dht_settings) - { - } -#endif #ifndef TORRENT_DISABLE_DHT if (m_dht && (flags & session::save_dht_state)) { @@ -3720,7 +3715,9 @@ namespace aux { if (mapping == m_tcp_mapping[map_transport] && port != 0) { - if (ip != address()) set_external_address(ip); + // TODO: report the proper address of the router + if (ip != address()) set_external_address(ip, source_router + , address()); if (!m_listen_sockets.empty()) { m_listen_sockets.front().external_address = ip; @@ -3994,10 +3991,6 @@ namespace aux { session_impl::~session_impl() { -#if defined BOOST_HAS_PTHREADS - TORRENT_ASSERT(!is_network_thread()); -#endif - #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) (*m_logger) << time_now_string() << "\n\n *** shutting down session *** \n\n"; #endif @@ -4026,7 +4019,7 @@ namespace aux { printf("\n==== Waiting to shut down: %d ==== \n\n", counter); } #endif - m_thread->join(); + if (m_thread) m_thread->join(); TORRENT_ASSERT(m_torrents.empty()); TORRENT_ASSERT(m_connections.empty()); @@ -4367,18 +4360,95 @@ namespace aux { m_upnp = 0; } - void session_impl::set_external_address(address const& ip) + bool session_impl::external_ip_t::add_vote(sha1_hash const& k, int type) + { + sources |= type; + if (voters.find(k)) return false; + voters.set(k); + ++num_votes; + return true; + } + + void session_impl::set_external_address(address const& ip + , int source_type, address const& source) { TORRENT_ASSERT(ip != address()); if (is_local(ip)) return; if (is_loopback(ip)) return; - if (m_external_address == ip) return; - // for now, just trust whoever tells us our external address first - if (m_external_address != address()) return; +#if defined TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << ": set_external_address(" << print_address(ip) + << ", " << source_type << ", " << print_address(source) << ")\n"; +#endif + // this is the key to use for the bloom filters + // it represents the identity of the voter + sha1_hash k; + hash_address(source, k); + + // do we already have an entry for this external IP? + std::vector::iterator i = std::find_if(m_external_addresses.begin() + , m_external_addresses.end(), boost::bind(&external_ip_t::addr, _1) == ip); + + if (i == m_external_addresses.end()) + { + // each IP only gets to add a new IP once + if (m_external_address_voters.find(k)) return; + + if (m_external_addresses.size() > 20) + { + if (rand() < RAND_MAX / 2) + { +#if defined TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << ": More than 20 slots, dopped\n"; +#endif + return; + } + // use stable sort here to maintain the fifo-order + // of the entries with the same number of votes + // this will sort in ascending order, i.e. the lowest + // votes first. Also, the oldest are first, so this + // is a sort of weighted LRU. + std::stable_sort(m_external_addresses.begin(), m_external_addresses.end()); + // erase the first element, since this is the + // oldest entry and the one with lowst number + // of votes. This makes sense because the oldest + // entry has had the longest time to receive more + // votes to be bumped up +#if defined TORRENT_VERBOSE_LOGGING + (*m_logger) << " More than 20 slots, dopping " + << print_address(m_external_addresses.front().addr) + << " (" << m_external_addresses.front().num_votes << ")\n"; +#endif + m_external_addresses.erase(m_external_addresses.begin()); + } + m_external_addresses.push_back(external_ip_t()); + i = m_external_addresses.end() - 1; + i->addr = ip; + } + // add one more vote to this external IP + if (!i->add_vote(k, source_type)) return; + + i = std::max_element(m_external_addresses.begin(), m_external_addresses.end()); + TORRENT_ASSERT(i != m_external_addresses.end()); + +#if defined TORRENT_VERBOSE_LOGGING + for (std::vector::iterator j = m_external_addresses.begin() + , end(m_external_addresses.end()); j != end; ++j) + { + (*m_logger) << ((j == i)?"-->":" ") + << print_address(j->addr) << " votes: " + << j->num_votes << "\n"; + } +#endif + if (i->addr == m_external_address) return; + +#if defined TORRENT_VERBOSE_LOGGING + (*m_logger) << " external IP updated\n"; +#endif + m_external_address = i->addr; + m_external_address_voters.clear(); - m_external_address = ip; if (m_alerts.should_post()) m_alerts.post_alert(external_ip_alert(ip)); diff --git a/src/socket_io.cpp b/src/socket_io.cpp index bcb6cb7eb..033ad067d 100644 --- a/src/socket_io.cpp +++ b/src/socket_io.cpp @@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/socket.hpp" #include "libtorrent/socket_io.hpp" #include "libtorrent/address.hpp" +#include "libtorrent/hasher.hpp" // for hasher namespace libtorrent { @@ -92,5 +93,21 @@ namespace libtorrent return print_endpoint(tcp::endpoint(ep.address(), ep.port())); } + void hash_address(address const& ip, sha1_hash& h) + { +#if TORRENT_USE_IPV6 + if (ip.is_v6()) + { + address_v6::bytes_type b = ip.to_v6().to_bytes(); + h = hasher((char*)&b[0], b.size()).final(); + } + else +#endif + { + address_v4::bytes_type b = ip.to_v4().to_bytes(); + h = hasher((char*)&b[0], b.size()).final(); + } + } + } diff --git a/src/torrent.cpp b/src/torrent.cpp index a8baede7a..834522bda 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1743,8 +1743,10 @@ namespace libtorrent INVARIANT_CHECK; TORRENT_ASSERT(r.kind == tracker_request::announce_request); + TORRENT_ASSERT(!tracker_ips.empty()); if (external_ip != address()) - m_ses.set_external_address(external_ip); + m_ses.set_external_address(external_ip, aux::session_impl::source_tracker + , *tracker_ips.begin()); ptime now = time_now(); diff --git a/test/test_primitives.cpp b/test/test_primitives.cpp index f6e2e147c..6ae3d2a16 100644 --- a/test/test_primitives.cpp +++ b/test/test_primitives.cpp @@ -48,6 +48,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/bencode.hpp" #include "libtorrent/timestamp_history.hpp" #include "libtorrent/enum_net.hpp" +#include "libtorrent/bloom_filter.hpp" +#include "libtorrent/aux_/session_impl.hpp" #ifndef TORRENT_DISABLE_DHT #include "libtorrent/kademlia/node_id.hpp" #include "libtorrent/kademlia/routing_table.hpp" @@ -379,12 +381,89 @@ namespace libtorrent TORRENT_EXPORT void find_control_url(int type, char const* string, parse_state& state); +address rand_v4() +{ + return address_v4(rand() << 16 | rand()); +} + int test_main() { using namespace libtorrent; error_code ec; int ret = 0; + // test external ip voting + aux::session_impl* ses = new aux::session_impl(std::pair(0,0) + , fingerprint("LT", 0, 0, 0, 0), "0.0.0.0" +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + , "" +#endif + ); + + // test a single malicious node + // adds 50 legitimate responses from different peers + // and 50 malicious responses from the same peer + address real_external = address_v4::from_string("5.5.5.5"); + address malicious = address_v4::from_string("4.4.4.4"); + for (int i = 0; i < 50; ++i) + { + ses->set_external_address(real_external, aux::session_impl::source_dht, rand_v4()); + ses->set_external_address(rand_v4(), aux::session_impl::source_dht, malicious); + } + TEST_CHECK(ses->external_address() == real_external); + ses->abort(); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + ses->m_logger.reset(); +#endif + delete ses; + ses = new aux::session_impl(std::pair(0,0) + , fingerprint("LT", 0, 0, 0, 0), "0.0.0.0" +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + , "" +#endif + ); + + // test a single malicious node + // adds 50 legitimate responses from different peers + // and 50 consistent malicious responses from the same peer + real_external = address_v4::from_string("5.5.5.5"); + malicious = address_v4::from_string("4.4.4.4"); + address malicious_external = address_v4::from_string("3.3.3.3"); + for (int i = 0; i < 50; ++i) + { + ses->set_external_address(real_external, aux::session_impl::source_dht, rand_v4()); + ses->set_external_address(malicious_external, aux::session_impl::source_dht, malicious); + } + TEST_CHECK(ses->external_address() == real_external); + ses->abort(); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + ses->m_logger.reset(); +#endif + delete ses; + + // test bloom_filter + bloom_filter<32> filter; + sha1_hash k1 = hasher("test1", 5).final(); + sha1_hash k2 = hasher("test2", 5).final(); + sha1_hash k3 = hasher("test3", 5).final(); + sha1_hash k4 = hasher("test4", 5).final(); + TEST_CHECK(!filter.find(k1)); + TEST_CHECK(!filter.find(k2)); + TEST_CHECK(!filter.find(k3)); + TEST_CHECK(!filter.find(k4)); + + filter.set(k1); + TEST_CHECK(filter.find(k1)); + TEST_CHECK(!filter.find(k2)); + TEST_CHECK(!filter.find(k3)); + TEST_CHECK(!filter.find(k4)); + + filter.set(k4); + TEST_CHECK(filter.find(k1)); + TEST_CHECK(!filter.find(k2)); + TEST_CHECK(!filter.find(k3)); + TEST_CHECK(filter.find(k4)); + // test timestamp_history { timestamp_history h;