/root/bitcoin/src/common/bloom.cpp
| Line | Count | Source | 
| 1 |  | // Copyright (c) 2012-present The Bitcoin Core 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 <common/bloom.h> | 
| 6 |  |  | 
| 7 |  | #include <hash.h> | 
| 8 |  | #include <primitives/transaction.h> | 
| 9 |  | #include <random.h> | 
| 10 |  | #include <script/script.h> | 
| 11 |  | #include <script/solver.h> | 
| 12 |  | #include <span.h> | 
| 13 |  | #include <streams.h> | 
| 14 |  | #include <util/fastrange.h> | 
| 15 |  |  | 
| 16 |  | #include <algorithm> | 
| 17 |  | #include <cmath> | 
| 18 |  | #include <cstdlib> | 
| 19 |  | #include <limits> | 
| 20 |  | #include <vector> | 
| 21 |  |  | 
| 22 |  | static constexpr double LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455; | 
| 23 |  | static constexpr double LN2 = 0.6931471805599453094172321214581765680755001343602552; | 
| 24 |  |  | 
| 25 |  | CBloomFilter::CBloomFilter(const unsigned int nElements, const double nFPRate, const unsigned int nTweakIn, unsigned char nFlagsIn) : | 
| 26 |  |     /** | 
| 27 |  |      * The ideal size for a bloom filter with a given number of elements and false positive rate is: | 
| 28 |  |      * - nElements * log(fp rate) / ln(2)^2 | 
| 29 |  |      * We ignore filter parameters which will create a bloom filter larger than the protocol limits | 
| 30 |  |      */ | 
| 31 | 0 |     vData(std::min((unsigned int)(-1  / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8), | 
| 32 |  |     /** | 
| 33 |  |      * The ideal number of hash functions is filter size * ln(2) / number of elements | 
| 34 |  |      * Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits | 
| 35 |  |      * See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas | 
| 36 |  |      */ | 
| 37 | 0 |     nHashFuncs(std::min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), | 
| 38 | 0 |     nTweak(nTweakIn), | 
| 39 | 0 |     nFlags(nFlagsIn) | 
| 40 | 0 | { | 
| 41 | 0 | } | 
| 42 |  |  | 
| 43 |  | inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, std::span<const unsigned char> vDataToHash) const | 
| 44 | 0 | { | 
| 45 |  |     // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values. | 
| 46 | 0 |     return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8); | 
| 47 | 0 | } | 
| 48 |  |  | 
| 49 |  | void CBloomFilter::insert(std::span<const unsigned char> vKey) | 
| 50 | 0 | { | 
| 51 | 0 |     if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700) | 
| 52 | 0 |         return; | 
| 53 | 0 |     for (unsigned int i = 0; i < nHashFuncs; i++) | 
| 54 | 0 |     { | 
| 55 | 0 |         unsigned int nIndex = Hash(i, vKey); | 
| 56 |  |         // Sets bit nIndex of vData | 
| 57 | 0 |         vData[nIndex >> 3] |= (1 << (7 & nIndex)); | 
| 58 | 0 |     } | 
| 59 | 0 | } | 
| 60 |  |  | 
| 61 |  | void CBloomFilter::insert(const COutPoint& outpoint) | 
| 62 | 0 | { | 
| 63 | 0 |     DataStream stream{}; | 
| 64 | 0 |     stream << outpoint; | 
| 65 | 0 |     insert(MakeUCharSpan(stream)); | 
| 66 | 0 | } | 
| 67 |  |  | 
| 68 |  | bool CBloomFilter::contains(std::span<const unsigned char> vKey) const | 
| 69 | 0 | { | 
| 70 | 0 |     if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700) | 
| 71 | 0 |         return true; | 
| 72 | 0 |     for (unsigned int i = 0; i < nHashFuncs; i++) | 
| 73 | 0 |     { | 
| 74 | 0 |         unsigned int nIndex = Hash(i, vKey); | 
| 75 |  |         // Checks bit nIndex of vData | 
| 76 | 0 |         if (!(vData[nIndex >> 3] & (1 << (7 & nIndex)))) | 
| 77 | 0 |             return false; | 
| 78 | 0 |     } | 
| 79 | 0 |     return true; | 
| 80 | 0 | } | 
| 81 |  |  | 
| 82 |  | bool CBloomFilter::contains(const COutPoint& outpoint) const | 
| 83 | 0 | { | 
| 84 | 0 |     DataStream stream{}; | 
| 85 | 0 |     stream << outpoint; | 
| 86 | 0 |     return contains(MakeUCharSpan(stream)); | 
| 87 | 0 | } | 
| 88 |  |  | 
| 89 |  | bool CBloomFilter::IsWithinSizeConstraints() const | 
| 90 | 0 | { | 
| 91 | 0 |     return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; | 
| 92 | 0 | } | 
| 93 |  |  | 
| 94 |  | bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) | 
| 95 | 0 | { | 
| 96 | 0 |     bool fFound = false; | 
| 97 |  |     // Match if the filter contains the hash of tx | 
| 98 |  |     //  for finding tx when they appear in a block | 
| 99 | 0 |     if (vData.empty()) // zero-size = "match-all" filter | 
| 100 | 0 |         return true; | 
| 101 | 0 |     const Txid& hash = tx.GetHash(); | 
| 102 | 0 |     if (contains(hash.ToUint256())) | 
| 103 | 0 |         fFound = true; | 
| 104 |  | 
 | 
| 105 | 0 |     for (unsigned int i = 0; i < tx.vout.size(); i++) | 
| 106 | 0 |     { | 
| 107 | 0 |         const CTxOut& txout = tx.vout[i]; | 
| 108 |  |         // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx | 
| 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. | 
| 112 | 0 |         CScript::const_iterator pc = txout.scriptPubKey.begin(); | 
| 113 | 0 |         std::vector<unsigned char> data; | 
| 114 | 0 |         while (pc < txout.scriptPubKey.end()) | 
| 115 | 0 |         { | 
| 116 | 0 |             opcodetype opcode; | 
| 117 | 0 |             if (!txout.scriptPubKey.GetOp(pc, opcode, data)) | 
| 118 | 0 |                 break; | 
| 119 | 0 |             if (data.size() != 0 && contains(data)) | 
| 120 | 0 |             { | 
| 121 | 0 |                 fFound = true; | 
| 122 | 0 |                 if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) | 
| 123 | 0 |                     insert(COutPoint(hash, i)); | 
| 124 | 0 |                 else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) | 
| 125 | 0 |                 { | 
| 126 | 0 |                     std::vector<std::vector<unsigned char> > vSolutions; | 
| 127 | 0 |                     TxoutType type = Solver(txout.scriptPubKey, vSolutions); | 
| 128 | 0 |                     if (type == TxoutType::PUBKEY || type == TxoutType::MULTISIG) { | 
| 129 | 0 |                         insert(COutPoint(hash, i)); | 
| 130 | 0 |                     } | 
| 131 | 0 |                 } | 
| 132 | 0 |                 break; | 
| 133 | 0 |             } | 
| 134 | 0 |         } | 
| 135 | 0 |     } | 
| 136 |  | 
 | 
| 137 | 0 |     if (fFound) | 
| 138 | 0 |         return true; | 
| 139 |  |  | 
| 140 | 0 |     for (const CTxIn& txin : tx.vin) | 
| 141 | 0 |     { | 
| 142 |  |         // Match if the filter contains an outpoint tx spends | 
| 143 | 0 |         if (contains(txin.prevout)) | 
| 144 | 0 |             return true; | 
| 145 |  |  | 
| 146 |  |         // Match if the filter contains any arbitrary script data element in any scriptSig in tx | 
| 147 | 0 |         CScript::const_iterator pc = txin.scriptSig.begin(); | 
| 148 | 0 |         std::vector<unsigned char> data; | 
| 149 | 0 |         while (pc < txin.scriptSig.end()) | 
| 150 | 0 |         { | 
| 151 | 0 |             opcodetype opcode; | 
| 152 | 0 |             if (!txin.scriptSig.GetOp(pc, opcode, data)) | 
| 153 | 0 |                 break; | 
| 154 | 0 |             if (data.size() != 0 && contains(data)) | 
| 155 | 0 |                 return true; | 
| 156 | 0 |         } | 
| 157 | 0 |     } | 
| 158 |  |  | 
| 159 | 0 |     return false; | 
| 160 | 0 | } | 
| 161 |  |  | 
| 162 |  | CRollingBloomFilter::CRollingBloomFilter(const unsigned int nElements, const double fpRate) | 
| 163 | 1.79k | { | 
| 164 | 1.79k |     double logFpRate = log(fpRate); | 
| 165 |  |     /* The optimal number of hash functions is log(fpRate) / log(0.5), but | 
| 166 |  |      * restrict it to the range 1-50. */ | 
| 167 | 1.79k |     nHashFuncs = std::max(1, std::min((int)round(logFpRate / log(0.5)), 50)); | 
| 168 |  |     /* In this rolling bloom filter, we'll store between 2 and 3 generations of nElements / 2 entries. */ | 
| 169 | 1.79k |     nEntriesPerGeneration = (nElements + 1) / 2; | 
| 170 | 1.79k |     uint32_t nMaxElements = nEntriesPerGeneration * 3; | 
| 171 |  |     /* The maximum fpRate = pow(1.0 - exp(-nHashFuncs * nMaxElements / nFilterBits), nHashFuncs) | 
| 172 |  |      * =>          pow(fpRate, 1.0 / nHashFuncs) = 1.0 - exp(-nHashFuncs * nMaxElements / nFilterBits) | 
| 173 |  |      * =>          1.0 - pow(fpRate, 1.0 / nHashFuncs) = exp(-nHashFuncs * nMaxElements / nFilterBits) | 
| 174 |  |      * =>          log(1.0 - pow(fpRate, 1.0 / nHashFuncs)) = -nHashFuncs * nMaxElements / nFilterBits | 
| 175 |  |      * =>          nFilterBits = -nHashFuncs * nMaxElements / log(1.0 - pow(fpRate, 1.0 / nHashFuncs)) | 
| 176 |  |      * =>          nFilterBits = -nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs)) | 
| 177 |  |      */ | 
| 178 | 1.79k |     uint32_t nFilterBits = (uint32_t)ceil(-1.0 * nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs))); | 
| 179 | 1.79k |     data.clear(); | 
| 180 |  |     /* For each data element we need to store 2 bits. If both bits are 0, the | 
| 181 |  |      * bit is treated as unset. If the bits are (01), (10), or (11), the bit is | 
| 182 |  |      * treated as set in generation 1, 2, or 3 respectively. | 
| 183 |  |      * These bits are stored in separate integers: position P corresponds to bit | 
| 184 |  |      * (P & 63) of the integers data[(P >> 6) * 2] and data[(P >> 6) * 2 + 1]. */ | 
| 185 | 1.79k |     data.resize(((nFilterBits + 63) / 64) << 1); | 
| 186 | 1.79k |     reset(); | 
| 187 | 1.79k | } | 
| 188 |  |  | 
| 189 |  | /* Similar to CBloomFilter::Hash */ | 
| 190 |  | static inline uint32_t RollingBloomHash(unsigned int nHashNum, uint32_t nTweak, std::span<const unsigned char> vDataToHash) | 
| 191 | 30.9M | { | 
| 192 | 30.9M |     return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash); | 
| 193 | 30.9M | } | 
| 194 |  |  | 
| 195 |  | void CRollingBloomFilter::insert(std::span<const unsigned char> vKey) | 
| 196 | 287k | { | 
| 197 | 287k |     if (nEntriesThisGeneration == nEntriesPerGeneration) { | 
| 198 | 0 |         nEntriesThisGeneration = 0; | 
| 199 | 0 |         nGeneration++; | 
| 200 | 0 |         if (nGeneration == 4) { | 
| 201 | 0 |             nGeneration = 1; | 
| 202 | 0 |         } | 
| 203 | 0 |         uint64_t nGenerationMask1 = 0 - (uint64_t)(nGeneration & 1); | 
| 204 | 0 |         uint64_t nGenerationMask2 = 0 - (uint64_t)(nGeneration >> 1); | 
| 205 |  |         /* Wipe old entries that used this generation number. */ | 
| 206 | 0 |         for (uint32_t p = 0; p < data.size(); p += 2) { | 
| 207 | 0 |             uint64_t p1 = data[p], p2 = data[p + 1]; | 
| 208 | 0 |             uint64_t mask = (p1 ^ nGenerationMask1) | (p2 ^ nGenerationMask2); | 
| 209 | 0 |             data[p] = p1 & mask; | 
| 210 | 0 |             data[p + 1] = p2 & mask; | 
| 211 | 0 |         } | 
| 212 | 0 |     } | 
| 213 | 287k |     nEntriesThisGeneration++; | 
| 214 |  |  | 
| 215 | 6.03M |     for (int n = 0; n < nHashFuncs; n++) { | 
| 216 | 5.74M |         uint32_t h = RollingBloomHash(n, nTweak, vKey); | 
| 217 | 5.74M |         int bit = h & 0x3F; | 
| 218 |  |         /* FastMod works with the upper bits of h, so it is safe to ignore that the lower bits of h are already used for bit. */ | 
| 219 | 5.74M |         uint32_t pos = FastRange32(h, data.size()); | 
| 220 |  |         /* The lowest bit of pos is ignored, and set to zero for the first bit, and to one for the second. */ | 
| 221 | 5.74M |         data[pos & ~1U] = (data[pos & ~1U] & ~(uint64_t{1} << bit)) | (uint64_t(nGeneration & 1)) << bit; | 
| 222 | 5.74M |         data[pos | 1] = (data[pos | 1] & ~(uint64_t{1} << bit)) | (uint64_t(nGeneration >> 1)) << bit; | 
| 223 | 5.74M |     } | 
| 224 | 287k | } | 
| 225 |  |  | 
| 226 |  | bool CRollingBloomFilter::contains(std::span<const unsigned char> vKey) const | 
| 227 | 19.2M | { | 
| 228 | 25.5M |     for (int n = 0; n < nHashFuncs; n++) { | 
| 229 | 25.2M |         uint32_t h = RollingBloomHash(n, nTweak, vKey); | 
| 230 | 25.2M |         int bit = h & 0x3F; | 
| 231 | 25.2M |         uint32_t pos = FastRange32(h, data.size()); | 
| 232 |  |         /* If the relevant bit is not set in either data[pos & ~1] or data[pos | 1], the filter does not contain vKey */ | 
| 233 | 25.2M |         if (!(((data[pos & ~1U] | data[pos | 1]) >> bit) & 1)) { | 
| 234 | 18.9M |             return false; | 
| 235 | 18.9M |         } | 
| 236 | 25.2M |     } | 
| 237 | 314k |     return true; | 
| 238 | 19.2M | } | 
| 239 |  |  | 
| 240 |  | void CRollingBloomFilter::reset() | 
| 241 | 125k | { | 
| 242 | 125k |     nTweak = FastRandomContext().rand<unsigned int>(); | 
| 243 | 125k |     nEntriesThisGeneration = 0; | 
| 244 | 125k |     nGeneration = 1; | 
| 245 | 125k |     std::fill(data.begin(), data.end(), 0); | 
| 246 | 125k | } |