]> Git Repo - VerusCoin.git/blob - src/bloom.cpp
Merge pull request #5398
[VerusCoin.git] / src / bloom.cpp
1 // Copyright (c) 2012-2014 The Bitcoin 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 "bloom.h"
6
7 #include "primitives/transaction.h"
8 #include "hash.h"
9 #include "script/script.h"
10 #include "script/standard.h"
11 #include "streams.h"
12
13 #include <math.h>
14 #include <stdlib.h>
15
16 #include <boost/foreach.hpp>
17
18 #define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
19 #define LN2 0.6931471805599453094172321214581765680755001343602552
20
21 using namespace std;
22
23 CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) :
24 /**
25  * The ideal size for a bloom filter with a given number of elements and false positive rate is:
26  * - nElements * log(fp rate) / ln(2)^2
27  * We ignore filter parameters which will create a bloom filter larger than the protocol limits
28  */
29 vData(min((unsigned int)(-1  / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8),
30 /**
31  * The ideal number of hash functions is filter size * ln(2) / number of elements
32  * Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits
33  * See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas
34  */
35 isFull(false),
36 isEmpty(false),
37 nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)),
38 nTweak(nTweakIn),
39 nFlags(nFlagsIn)
40 {
41 }
42
43 inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const
44 {
45     // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values.
46     return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8);
47 }
48
49 void CBloomFilter::insert(const vector<unsigned char>& vKey)
50 {
51     if (isFull)
52         return;
53     for (unsigned int i = 0; i < nHashFuncs; i++)
54     {
55         unsigned int nIndex = Hash(i, vKey);
56         // Sets bit nIndex of vData
57         vData[nIndex >> 3] |= (1 << (7 & nIndex));
58     }
59     isEmpty = false;
60 }
61
62 void CBloomFilter::insert(const COutPoint& outpoint)
63 {
64     CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
65     stream << outpoint;
66     vector<unsigned char> data(stream.begin(), stream.end());
67     insert(data);
68 }
69
70 void CBloomFilter::insert(const uint256& hash)
71 {
72     vector<unsigned char> data(hash.begin(), hash.end());
73     insert(data);
74 }
75
76 bool CBloomFilter::contains(const vector<unsigned char>& vKey) const
77 {
78     if (isFull)
79         return true;
80     if (isEmpty)
81         return false;
82     for (unsigned int i = 0; i < nHashFuncs; i++)
83     {
84         unsigned int nIndex = Hash(i, vKey);
85         // Checks bit nIndex of vData
86         if (!(vData[nIndex >> 3] & (1 << (7 & nIndex))))
87             return false;
88     }
89     return true;
90 }
91
92 bool CBloomFilter::contains(const COutPoint& outpoint) const
93 {
94     CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
95     stream << outpoint;
96     vector<unsigned char> data(stream.begin(), stream.end());
97     return contains(data);
98 }
99
100 bool CBloomFilter::contains(const uint256& hash) const
101 {
102     vector<unsigned char> data(hash.begin(), hash.end());
103     return contains(data);
104 }
105
106 void CBloomFilter::clear()
107 {
108     vData.assign(vData.size(),0);
109     isFull = false;
110     isEmpty = true;
111 }
112
113 bool CBloomFilter::IsWithinSizeConstraints() const
114 {
115     return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS;
116 }
117
118 bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
119 {
120     bool fFound = false;
121     // Match if the filter contains the hash of tx
122     //  for finding tx when they appear in a block
123     if (isFull)
124         return true;
125     if (isEmpty)
126         return false;
127     const uint256& hash = tx.GetHash();
128     if (contains(hash))
129         fFound = true;
130
131     for (unsigned int i = 0; i < tx.vout.size(); i++)
132     {
133         const CTxOut& txout = tx.vout[i];
134         // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx
135         // If this matches, also add the specific output that was matched.
136         // This means clients don't have to update the filter themselves when a new relevant tx 
137         // is discovered in order to find spending transactions, which avoids round-tripping and race conditions.
138         CScript::const_iterator pc = txout.scriptPubKey.begin();
139         vector<unsigned char> data;
140         while (pc < txout.scriptPubKey.end())
141         {
142             opcodetype opcode;
143             if (!txout.scriptPubKey.GetOp(pc, opcode, data))
144                 break;
145             if (data.size() != 0 && contains(data))
146             {
147                 fFound = true;
148                 if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL)
149                     insert(COutPoint(hash, i));
150                 else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY)
151                 {
152                     txnouttype type;
153                     vector<vector<unsigned char> > vSolutions;
154                     if (Solver(txout.scriptPubKey, type, vSolutions) &&
155                             (type == TX_PUBKEY || type == TX_MULTISIG))
156                         insert(COutPoint(hash, i));
157                 }
158                 break;
159             }
160         }
161     }
162
163     if (fFound)
164         return true;
165
166     BOOST_FOREACH(const CTxIn& txin, tx.vin)
167     {
168         // Match if the filter contains an outpoint tx spends
169         if (contains(txin.prevout))
170             return true;
171
172         // Match if the filter contains any arbitrary script data element in any scriptSig in tx
173         CScript::const_iterator pc = txin.scriptSig.begin();
174         vector<unsigned char> data;
175         while (pc < txin.scriptSig.end())
176         {
177             opcodetype opcode;
178             if (!txin.scriptSig.GetOp(pc, opcode, data))
179                 break;
180             if (data.size() != 0 && contains(data))
181                 return true;
182         }
183     }
184
185     return false;
186 }
187
188 void CBloomFilter::UpdateEmptyFull()
189 {
190     bool full = true;
191     bool empty = true;
192     for (unsigned int i = 0; i < vData.size(); i++)
193     {
194         full &= vData[i] == 0xff;
195         empty &= vData[i] == 0;
196     }
197     isFull = full;
198     isEmpty = empty;
199 }
This page took 0.037807 seconds and 4 git commands to generate.