]>
Commit | Line | Data |
---|---|---|
fa94b9d5 MF |
1 | // Copyright (c) 2012-2014 The Bitcoin developers |
2 | // Distributed under the MIT software license, see the accompanying | |
bd21612c | 3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
bd21612c MC |
4 | |
5 | #include "bloom.h" | |
51ed9ec9 | 6 | |
4a3587d8 | 7 | #include "core/transaction.h" |
d2e74c55 | 8 | #include "hash.h" |
c4408a6c | 9 | #include "script/script.h" |
10 | #include "script/standard.h" | |
fa736190 | 11 | #include "streams.h" |
bd21612c | 12 | |
51ed9ec9 BD |
13 | #include <math.h> |
14 | #include <stdlib.h> | |
15 | ||
53efb09e | 16 | #include <boost/foreach.hpp> |
17 | ||
bd21612c MC |
18 | #define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455 |
19 | #define LN2 0.6931471805599453094172321214581765680755001343602552 | |
20 | ||
21 | using namespace std; | |
22 | ||
e1a4f377 | 23 | CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) : |
fa94b9d5 MF |
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 | */ | |
bd21612c | 29 | vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8), |
fa94b9d5 MF |
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 | */ | |
37c6389c GM |
35 | isFull(false), |
36 | isEmpty(false), | |
b1f99bed | 37 | nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), |
e1a4f377 MC |
38 | nTweak(nTweakIn), |
39 | nFlags(nFlagsIn) | |
bd21612c MC |
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. | |
b1f99bed | 46 | return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8); |
bd21612c MC |
47 | } |
48 | ||
49 | void CBloomFilter::insert(const vector<unsigned char>& vKey) | |
50 | { | |
37c6389c | 51 | if (isFull) |
cbfc7735 | 52 | return; |
bd21612c MC |
53 | for (unsigned int i = 0; i < nHashFuncs; i++) |
54 | { | |
55 | unsigned int nIndex = Hash(i, vKey); | |
56 | // Sets bit nIndex of vData | |
b1b9c762 | 57 | vData[nIndex >> 3] |= (1 << (7 & nIndex)); |
bd21612c | 58 | } |
37c6389c | 59 | isEmpty = false; |
bd21612c MC |
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 | { | |
37c6389c | 78 | if (isFull) |
cbfc7735 | 79 | return true; |
37c6389c GM |
80 | if (isEmpty) |
81 | return false; | |
bd21612c MC |
82 | for (unsigned int i = 0; i < nHashFuncs; i++) |
83 | { | |
84 | unsigned int nIndex = Hash(i, vKey); | |
85 | // Checks bit nIndex of vData | |
b1b9c762 | 86 | if (!(vData[nIndex >> 3] & (1 << (7 & nIndex)))) |
bd21612c MC |
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 | ||
9c347313 TH |
106 | void CBloomFilter::clear() |
107 | { | |
108 | vData.assign(vData.size(),0); | |
109 | isFull = false; | |
110 | isEmpty = true; | |
111 | } | |
112 | ||
bd21612c MC |
113 | bool CBloomFilter::IsWithinSizeConstraints() const |
114 | { | |
115 | return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; | |
116 | } | |
117 | ||
d38da59b | 118 | bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) |
bd21612c | 119 | { |
d3b26f70 | 120 | bool fFound = false; |
bd21612c MC |
121 | // Match if the filter contains the hash of tx |
122 | // for finding tx when they appear in a block | |
37c6389c GM |
123 | if (isFull) |
124 | return true; | |
125 | if (isEmpty) | |
126 | return false; | |
d38da59b | 127 | const uint256& hash = tx.GetHash(); |
269d9c64 | 128 | if (contains(hash)) |
d3b26f70 | 129 | fFound = true; |
bd21612c | 130 | |
d3b26f70 | 131 | for (unsigned int i = 0; i < tx.vout.size(); i++) |
bd21612c | 132 | { |
d3b26f70 | 133 | const CTxOut& txout = tx.vout[i]; |
bd21612c | 134 | // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx |
d3b26f70 MC |
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. | |
bd21612c MC |
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)) | |
d3b26f70 MC |
146 | { |
147 | fFound = true; | |
e1a4f377 MC |
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 | } | |
d3b26f70 MC |
158 | break; |
159 | } | |
bd21612c MC |
160 | } |
161 | } | |
162 | ||
d3b26f70 MC |
163 | if (fFound) |
164 | return true; | |
165 | ||
bd21612c MC |
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 | } | |
37c6389c GM |
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 | } |