]>
Commit | Line | Data |
---|---|---|
bd21612c MC |
1 | // Copyright (c) 2012 The Bitcoin developers |
2 | // Distributed under the MIT/X11 software license, see the accompanying | |
3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | |
4 | #include <math.h> | |
5 | #include <stdlib.h> | |
6 | ||
7 | #include "bloom.h" | |
8 | #include "main.h" | |
9 | #include "script.h" | |
10 | ||
11 | #define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455 | |
12 | #define LN2 0.6931471805599453094172321214581765680755001343602552 | |
13 | ||
14 | using namespace std; | |
15 | ||
16 | static const unsigned char bit_mask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; | |
17 | ||
e1a4f377 | 18 | CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) : |
bd21612c MC |
19 | // The ideal size for a bloom filter with a given number of elements and false positive rate is: |
20 | // - nElements * log(fp rate) / ln(2)^2 | |
21 | // We ignore filter parameters which will create a bloom filter larger than the protocol limits | |
22 | vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8), | |
23 | // The ideal number of hash functions is filter size * ln(2) / number of elements | |
24 | // Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits | |
25 | // See http://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas | |
b1f99bed | 26 | nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), |
e1a4f377 MC |
27 | nTweak(nTweakIn), |
28 | nFlags(nFlagsIn) | |
bd21612c MC |
29 | { |
30 | } | |
31 | ||
32 | inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const | |
33 | { | |
34 | // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values. | |
b1f99bed | 35 | return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8); |
bd21612c MC |
36 | } |
37 | ||
38 | void CBloomFilter::insert(const vector<unsigned char>& vKey) | |
39 | { | |
cbfc7735 MC |
40 | if (vData.size() == 1 && vData[0] == 0xff) |
41 | return; | |
bd21612c MC |
42 | for (unsigned int i = 0; i < nHashFuncs; i++) |
43 | { | |
44 | unsigned int nIndex = Hash(i, vKey); | |
45 | // Sets bit nIndex of vData | |
46 | vData[nIndex >> 3] |= bit_mask[7 & nIndex]; | |
47 | } | |
48 | } | |
49 | ||
50 | void CBloomFilter::insert(const COutPoint& outpoint) | |
51 | { | |
52 | CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); | |
53 | stream << outpoint; | |
54 | vector<unsigned char> data(stream.begin(), stream.end()); | |
55 | insert(data); | |
56 | } | |
57 | ||
58 | void CBloomFilter::insert(const uint256& hash) | |
59 | { | |
60 | vector<unsigned char> data(hash.begin(), hash.end()); | |
61 | insert(data); | |
62 | } | |
63 | ||
64 | bool CBloomFilter::contains(const vector<unsigned char>& vKey) const | |
65 | { | |
cbfc7735 MC |
66 | if (vData.size() == 1 && vData[0] == 0xff) |
67 | return true; | |
bd21612c MC |
68 | for (unsigned int i = 0; i < nHashFuncs; i++) |
69 | { | |
70 | unsigned int nIndex = Hash(i, vKey); | |
71 | // Checks bit nIndex of vData | |
72 | if (!(vData[nIndex >> 3] & bit_mask[7 & nIndex])) | |
73 | return false; | |
74 | } | |
75 | return true; | |
76 | } | |
77 | ||
78 | bool CBloomFilter::contains(const COutPoint& outpoint) const | |
79 | { | |
80 | CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); | |
81 | stream << outpoint; | |
82 | vector<unsigned char> data(stream.begin(), stream.end()); | |
83 | return contains(data); | |
84 | } | |
85 | ||
86 | bool CBloomFilter::contains(const uint256& hash) const | |
87 | { | |
88 | vector<unsigned char> data(hash.begin(), hash.end()); | |
89 | return contains(data); | |
90 | } | |
91 | ||
92 | bool CBloomFilter::IsWithinSizeConstraints() const | |
93 | { | |
94 | return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; | |
95 | } | |
96 | ||
d3b26f70 | 97 | bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash) |
bd21612c | 98 | { |
d3b26f70 | 99 | bool fFound = false; |
bd21612c MC |
100 | // Match if the filter contains the hash of tx |
101 | // for finding tx when they appear in a block | |
269d9c64 | 102 | if (contains(hash)) |
d3b26f70 | 103 | fFound = true; |
bd21612c | 104 | |
d3b26f70 | 105 | for (unsigned int i = 0; i < tx.vout.size(); i++) |
bd21612c | 106 | { |
d3b26f70 | 107 | const CTxOut& txout = tx.vout[i]; |
bd21612c | 108 | // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx |
d3b26f70 MC |
109 | // If this matches, also add the specific output that was matched. |
110 | // This means clients don't have to update the filter themselves when a new relevant tx | |
111 | // is discovered in order to find spending transactions, which avoids round-tripping and race conditions. | |
bd21612c MC |
112 | CScript::const_iterator pc = txout.scriptPubKey.begin(); |
113 | vector<unsigned char> data; | |
114 | while (pc < txout.scriptPubKey.end()) | |
115 | { | |
116 | opcodetype opcode; | |
117 | if (!txout.scriptPubKey.GetOp(pc, opcode, data)) | |
118 | break; | |
119 | if (data.size() != 0 && contains(data)) | |
d3b26f70 MC |
120 | { |
121 | fFound = true; | |
e1a4f377 MC |
122 | if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) |
123 | insert(COutPoint(hash, i)); | |
124 | else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) | |
125 | { | |
126 | txnouttype type; | |
127 | vector<vector<unsigned char> > vSolutions; | |
128 | if (Solver(txout.scriptPubKey, type, vSolutions) && | |
129 | (type == TX_PUBKEY || type == TX_MULTISIG)) | |
130 | insert(COutPoint(hash, i)); | |
131 | } | |
d3b26f70 MC |
132 | break; |
133 | } | |
bd21612c MC |
134 | } |
135 | } | |
136 | ||
d3b26f70 MC |
137 | if (fFound) |
138 | return true; | |
139 | ||
bd21612c MC |
140 | BOOST_FOREACH(const CTxIn& txin, tx.vin) |
141 | { | |
142 | // Match if the filter contains an outpoint tx spends | |
143 | if (contains(txin.prevout)) | |
144 | return true; | |
145 | ||
146 | // Match if the filter contains any arbitrary script data element in any scriptSig in tx | |
147 | CScript::const_iterator pc = txin.scriptSig.begin(); | |
148 | vector<unsigned char> data; | |
149 | while (pc < txin.scriptSig.end()) | |
150 | { | |
151 | opcodetype opcode; | |
152 | if (!txin.scriptSig.GetOp(pc, opcode, data)) | |
153 | break; | |
154 | if (data.size() != 0 && contains(data)) | |
155 | return true; | |
156 | } | |
157 | } | |
158 | ||
159 | return false; | |
160 | } |