Browse Source

Merge pull request #2170 from gluk256/_bloom_filter

bloom filter
cl-refactor
Gav Wood 10 years ago
parent
commit
a8d33fab39
  1. 64
      libwhisper/BloomFilter.cpp
  2. 62
      libwhisper/BloomFilter.h
  3. 25
      libwhisper/Common.cpp
  4. 15
      libwhisper/Common.h
  5. 4
      libwhisper/WhisperHost.cpp
  6. 4
      libwhisper/WhisperHost.h
  7. 182
      test/libwhisper/bloomFilter.cpp
  8. 15
      test/libwhisper/whisperTopic.cpp

64
libwhisper/BloomFilter.cpp

@ -0,0 +1,64 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BloomFilter.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date June 2015
*/
#include "BloomFilter.h"
using namespace std;
using namespace dev;
using namespace dev::shh;
static unsigned const c_mask[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
void TopicBloomFilter::addRaw(AbridgedTopic const& _h)
{
*this |= _h;
for (unsigned i = 0; i < CounterSize; ++i)
if (isBitSet(_h, i))
{
if (m_refCounter[i] != numeric_limits<uint16_t>::max())
m_refCounter[i]++;
else
BOOST_THROW_EXCEPTION(Overflow());
}
}
void TopicBloomFilter::removeRaw(AbridgedTopic const& _h)
{
for (unsigned i = 0; i < CounterSize; ++i)
if (isBitSet(_h, i))
{
if (m_refCounter[i])
m_refCounter[i]--;
if (!m_refCounter[i])
(*this)[i / 8] &= ~c_mask[i % 8];
}
}
bool TopicBloomFilter::isBitSet(AbridgedTopic const& _h, unsigned _index)
{
unsigned iByte = _index / 8;
unsigned iBit = _index % 8;
return (_h[iByte] & c_mask[iBit]) != 0;
}

62
libwhisper/BloomFilter.h

@ -0,0 +1,62 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file BloomFilter.h
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date June 2015
*/
#pragma once
#include "Common.h"
namespace dev
{
namespace shh
{
class TopicBloomFilter: public AbridgedTopic
{
public:
TopicBloomFilter() { init(); }
TopicBloomFilter(AbridgedTopic const& _h): AbridgedTopic(_h) { init(); }
void addBloom(AbridgedTopic const& _h) { addRaw(_h.template bloomPart<BitsPerBloom, 4>()); }
void removeBloom(AbridgedTopic const& _h) { removeRaw(_h.template bloomPart<BitsPerBloom, 4>()); }
bool containsBloom(AbridgedTopic const& _h) const { return contains(_h.template bloomPart<BitsPerBloom, 4>()); }
void addRaw(AbridgedTopic const& _h);
void removeRaw(AbridgedTopic const& _h);
bool containsRaw(AbridgedTopic const& _h) const { return contains(_h); }
enum { BitsPerBloom = 3 };
private:
void init() { for (unsigned i = 0; i < CounterSize; ++i) m_refCounter[i] = 0; }
static bool isBitSet(AbridgedTopic const& _h, unsigned _index);
enum { CounterSize = 8 * TopicBloomFilter::size };
std::array<uint16_t, CounterSize> m_refCounter;
};
}
}

25
libwhisper/Common.cpp

@ -20,9 +20,10 @@
*/
#include "Common.h"
#include <libdevcore/SHA3.h>
#include "Message.h"
#include "BloomFilter.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
@ -37,7 +38,7 @@ AbridgedTopics dev::shh::abridge(Topics const& _topics)
{
AbridgedTopics ret;
ret.reserve(_topics.size());
for (auto const& t : _topics)
for (auto const& t: _topics)
ret.push_back(abridge(t));
return ret;
}
@ -84,6 +85,26 @@ bool TopicFilter::matches(Envelope const& _e) const
return false;
}
TopicFilter::TopicFilter(RLP const& _r)
{
for (RLP const& i: _r)
{
m_topicMasks.push_back(TopicMask());
for (RLP const& j: i)
m_topicMasks.back().push_back(j.toPair<FixedHash<4>, FixedHash<4>>());
}
}
AbridgedTopic TopicFilter::exportBloom() const
{
AbridgedTopic ret;
for (TopicMask const& t: m_topicMasks)
for (auto const& i: t)
ret |= i.first.template bloomPart<TopicBloomFilter::BitsPerBloom, TopicBloomFilter::size>();
return ret;
}
TopicMask BuildTopicMask::toTopicMask() const
{
TopicMask ret;

15
libwhisper/Common.h

@ -48,7 +48,6 @@ using h256Set = dev::h256Set;
class WhisperHost;
class WhisperPeer;
class Whisper;
class Envelope;
enum WhisperPacket
@ -91,7 +90,7 @@ protected:
h256s m_parts;
};
using TopicMask = std::vector<std::pair<AbridgedTopic, AbridgedTopic>>;
using TopicMask = std::vector<std::pair<AbridgedTopic, AbridgedTopic>>; // where pair::first is the actual abridged topic hash, pair::second is a constant (probably redundunt)
using TopicMasks = std::vector<TopicMask>;
class TopicFilter
@ -101,20 +100,12 @@ public:
TopicFilter(Topics const& _m) { m_topicMasks.push_back(TopicMask()); for (auto const& h: _m) m_topicMasks.back().push_back(std::make_pair(abridge(h), h ? ~AbridgedTopic() : AbridgedTopic())); }
TopicFilter(TopicMask const& _m): m_topicMasks(1, _m) {}
TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {}
TopicFilter(RLP const& _r)//: m_topicMasks(_r.toVector<std::vector<>>())
{
for (RLP i: _r)
{
m_topicMasks.push_back(TopicMask());
for (RLP j: i)
m_topicMasks.back().push_back(j.toPair<FixedHash<4>, FixedHash<4>>());
}
}
TopicFilter(RLP const& _r);
void streamRLP(RLPStream& _s) const { _s << m_topicMasks; }
h256 sha3() const;
bool matches(Envelope const& _m) const;
AbridgedTopic exportBloom() const;
private:
TopicMasks m_topicMasks;

4
libwhisper/WhisperHost.cpp

@ -113,6 +113,7 @@ unsigned WhisperHost::installWatch(shh::Topics const& _t)
if (!m_filters.count(h))
m_filters.insert(make_pair(h, f));
m_bloom.addRaw(f.filter.exportBloom());
return installWatchOnId(h);
}
@ -151,8 +152,11 @@ void WhisperHost::uninstallWatch(unsigned _i)
auto fit = m_filters.find(id);
if (fit != m_filters.end())
{
m_bloom.removeRaw(fit->second.filter.exportBloom());
if (!--fit->second.refCount)
m_filters.erase(fit);
}
}
void WhisperHost::doWork()

4
libwhisper/WhisperHost.h

@ -34,6 +34,7 @@
#include "Common.h"
#include "WhisperPeer.h"
#include "Interface.h"
#include "BloomFilter.h"
namespace dev
{
@ -60,7 +61,7 @@ public:
virtual void uninstallWatch(unsigned _watchId) override;
virtual h256s peekWatch(unsigned _watchId) const override { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } }
virtual h256s checkWatch(unsigned _watchId) override { cleanup(); dev::Guard l(m_filterLock); h256s ret; try { ret = m_watches.at(_watchId).changes; m_watches.at(_watchId).changes.clear(); } catch (...) {} return ret; }
virtual h256s watchMessages(unsigned _watchId) override;
virtual h256s watchMessages(unsigned _watchId) override; /// returns IDs of messages, which match specific watch criteria
virtual Envelope envelope(h256 _m) const override { try { dev::ReadGuard l(x_messages); return m_messages.at(_m); } catch (...) { return Envelope(); } }
@ -86,6 +87,7 @@ private:
mutable dev::Mutex m_filterLock;
std::map<h256, InstalledFilter> m_filters;
std::map<unsigned, ClientWatch> m_watches;
TopicBloomFilter m_bloom;
};
}

182
test/libwhisper/bloomFilter.cpp

@ -0,0 +1,182 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file whisperMessage.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date June 2015
*/
#include <boost/test/unit_test.hpp>
#include <libdevcore/SHA3.h>
#include <libwhisper/BloomFilter.h>
using namespace std;
using namespace dev;
using namespace dev::shh;
void testAddNonExisting(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
BOOST_REQUIRE(!_f.containsRaw(_h));
_f.addRaw(_h);
BOOST_REQUIRE(_f.containsRaw(_h));
}
void testRemoveExisting(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
BOOST_REQUIRE(_f.containsRaw(_h));
_f.removeRaw(_h);
BOOST_REQUIRE(!_f.containsRaw(_h));
}
void testAddNonExistingBloom(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
BOOST_REQUIRE(!_f.containsBloom(_h));
_f.addBloom(_h);
BOOST_REQUIRE(_f.containsBloom(_h));
}
void testRemoveExistingBloom(TopicBloomFilter& _f, AbridgedTopic const& _h)
{
BOOST_REQUIRE(_f.containsBloom(_h));
_f.removeBloom(_h);
BOOST_REQUIRE(!_f.containsBloom(_h));
}
BOOST_AUTO_TEST_SUITE(bloomFilter)
BOOST_AUTO_TEST_CASE(bloomFilterRandom)
{
VerbosityHolder setTemporaryLevel(10);
cnote << "Testing Bloom Filter matching...";
TopicBloomFilter f;
vector<AbridgedTopic> vec;
Topic x(0xDEADBEEF);
int const c_rounds = 4;
for (int i = 0; i < c_rounds; ++i, x = sha3(x))
vec.push_back(abridge(x));
for (int i = 0; i < c_rounds; ++i)
testAddNonExisting(f, vec[i]);
for (int i = 0; i < c_rounds; ++i)
testRemoveExisting(f, vec[i]);
for (int i = 0; i < c_rounds; ++i)
testAddNonExistingBloom(f, vec[i]);
for (int i = 0; i < c_rounds; ++i)
testRemoveExistingBloom(f, vec[i]);
}
BOOST_AUTO_TEST_CASE(bloomFilterRaw)
{
VerbosityHolder setTemporaryLevel(10);
cnote << "Testing Raw Bloom matching...";
TopicBloomFilter f;
AbridgedTopic b00000001(0x01);
AbridgedTopic b00010000(0x10);
AbridgedTopic b00011000(0x18);
AbridgedTopic b00110000(0x30);
AbridgedTopic b00110010(0x32);
AbridgedTopic b00111000(0x38);
AbridgedTopic b00000110(0x06);
AbridgedTopic b00110110(0x36);
AbridgedTopic b00110111(0x37);
testAddNonExisting(f, b00000001);
testAddNonExisting(f, b00010000);
testAddNonExisting(f, b00011000);
testAddNonExisting(f, b00110000);
BOOST_REQUIRE(f.contains(b00111000));
testAddNonExisting(f, b00110010);
testAddNonExisting(f, b00000110);
BOOST_REQUIRE(f.contains(b00110110));
BOOST_REQUIRE(f.contains(b00110111));
f.removeRaw(b00000001);
f.removeRaw(b00000001);
f.removeRaw(b00000001);
BOOST_REQUIRE(!f.contains(b00000001));
BOOST_REQUIRE(f.contains(b00010000));
BOOST_REQUIRE(f.contains(b00011000));
BOOST_REQUIRE(f.contains(b00110000));
BOOST_REQUIRE(f.contains(b00110010));
BOOST_REQUIRE(f.contains(b00111000));
BOOST_REQUIRE(f.contains(b00000110));
BOOST_REQUIRE(f.contains(b00110110));
BOOST_REQUIRE(!f.contains(b00110111));
f.removeRaw(b00010000);
BOOST_REQUIRE(!f.contains(b00000001));
BOOST_REQUIRE(f.contains(b00010000));
BOOST_REQUIRE(f.contains(b00011000));
BOOST_REQUIRE(f.contains(b00110000));
BOOST_REQUIRE(f.contains(b00110010));
BOOST_REQUIRE(f.contains(b00111000));
BOOST_REQUIRE(f.contains(b00000110));
BOOST_REQUIRE(f.contains(b00110110));
BOOST_REQUIRE(!f.contains(b00110111));
f.removeRaw(b00111000);
BOOST_REQUIRE(!f.contains(b00000001));
BOOST_REQUIRE(f.contains(b00010000));
BOOST_REQUIRE(!f.contains(b00011000));
BOOST_REQUIRE(f.contains(b00110000));
BOOST_REQUIRE(f.contains(b00110010));
BOOST_REQUIRE(!f.contains(b00111000));
BOOST_REQUIRE(f.contains(b00000110));
BOOST_REQUIRE(f.contains(b00110110));
BOOST_REQUIRE(!f.contains(b00110111));
f.addRaw(b00000001);
BOOST_REQUIRE(f.contains(b00000001));
BOOST_REQUIRE(f.contains(b00010000));
BOOST_REQUIRE(!f.contains(b00011000));
BOOST_REQUIRE(f.contains(b00110000));
BOOST_REQUIRE(f.contains(b00110010));
BOOST_REQUIRE(!f.contains(b00111000));
BOOST_REQUIRE(f.contains(b00000110));
BOOST_REQUIRE(f.contains(b00110110));
BOOST_REQUIRE(f.contains(b00110111));
f.removeRaw(b00110111);
BOOST_REQUIRE(!f.contains(b00000001));
BOOST_REQUIRE(f.contains(b00010000));
BOOST_REQUIRE(!f.contains(b00011000));
BOOST_REQUIRE(!f.contains(b00110000));
BOOST_REQUIRE(!f.contains(b00110010));
BOOST_REQUIRE(!f.contains(b00111000));
BOOST_REQUIRE(!f.contains(b00000110));
BOOST_REQUIRE(!f.contains(b00110110));
BOOST_REQUIRE(!f.contains(b00110111));
f.removeRaw(b00110111);
BOOST_REQUIRE(!f.contains(b00000001));
BOOST_REQUIRE(!f.contains(b00010000));
BOOST_REQUIRE(!f.contains(b00011000));
BOOST_REQUIRE(!f.contains(b00110000));
BOOST_REQUIRE(!f.contains(b00110010));
BOOST_REQUIRE(!f.contains(b00111000));
BOOST_REQUIRE(!f.contains(b00000110));
BOOST_REQUIRE(!f.contains(b00110110));
BOOST_REQUIRE(!f.contains(b00110111));
}
BOOST_AUTO_TEST_SUITE_END()

15
test/libwhisper/whisperTopic.cpp

@ -41,8 +41,7 @@ BOOST_FIXTURE_TEST_SUITE(whisper, P2PFixture)
BOOST_AUTO_TEST_CASE(topic)
{
cnote << "Testing Whisper...";
auto oldLogVerbosity = g_logVerbosity;
g_logVerbosity = 0;
VerbosityHolder setTemporaryLevel(0);
Host host1("Test", NetworkPreferences("127.0.0.1", 30303, false));
host1.setIdealPeerCount(1);
@ -99,16 +98,13 @@ BOOST_AUTO_TEST_CASE(topic)
}
listener.join();
g_logVerbosity = oldLogVerbosity;
BOOST_REQUIRE_EQUAL(result, 1 + 9 + 25 + 49 + 81);
}
BOOST_AUTO_TEST_CASE(forwarding)
{
cnote << "Testing Whisper forwarding...";
auto oldLogVerbosity = g_logVerbosity;
g_logVerbosity = 0;
VerbosityHolder setTemporaryLevel(0);
// Host must be configured not to share peers.
Host host1("Listner", NetworkPreferences("127.0.0.1", 30303, false));
@ -202,16 +198,13 @@ BOOST_AUTO_TEST_CASE(forwarding)
listener.join();
done = true;
forwarder.join();
g_logVerbosity = oldLogVerbosity;
BOOST_REQUIRE_EQUAL(result, 1);
}
BOOST_AUTO_TEST_CASE(asyncforwarding)
{
cnote << "Testing Whisper async forwarding...";
auto oldLogVerbosity = g_logVerbosity;
g_logVerbosity = 2;
VerbosityHolder setTemporaryLevel(2);
unsigned result = 0;
bool done = false;
@ -294,8 +287,6 @@ BOOST_AUTO_TEST_CASE(asyncforwarding)
done = true;
forwarder.join();
g_logVerbosity = oldLogVerbosity;
BOOST_REQUIRE_EQUAL(result, 1);
}

Loading…
Cancel
Save