/Users/eugenesiegel/btc/bitcoin/src/blockencodings.cpp
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | // Copyright (c) 2016-2022 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 <blockencodings.h> | 
| 6 |  | #include <chainparams.h> | 
| 7 |  | #include <common/system.h> | 
| 8 |  | #include <consensus/consensus.h> | 
| 9 |  | #include <consensus/validation.h> | 
| 10 |  | #include <crypto/sha256.h> | 
| 11 |  | #include <crypto/siphash.h> | 
| 12 |  | #include <logging.h> | 
| 13 |  | #include <random.h> | 
| 14 |  | #include <streams.h> | 
| 15 |  | #include <txmempool.h> | 
| 16 |  | #include <validation.h> | 
| 17 |  |  | 
| 18 |  | #include <unordered_map> | 
| 19 |  |  | 
| 20 |  | CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, const uint64_t nonce) : | 
| 21 | 1.61M |         nonce(nonce), | 
| 22 | 1.61M |         shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) { | 
| 23 | 1.61M |     FillShortTxIDSelector(); | 
| 24 |  |     //TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase | 
| 25 | 1.61M |     prefilledtxn[0] = {0, block.vtx[0]}; | 
| 26 | 13.4M |     for (size_t i = 1; i < block.vtx.size(); i++11.8M) { | 
| 27 | 11.8M |         const CTransaction& tx = *block.vtx[i]; | 
| 28 | 11.8M |         shorttxids[i - 1] = GetShortID(tx.GetWitnessHash()); | 
| 29 | 11.8M |     } | 
| 30 | 1.61M | } | 
| 31 |  |  | 
| 32 | 2.96M | void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const { | 
| 33 | 2.96M |     DataStream stream{}; | 
| 34 | 2.96M |     stream << header << nonce; | 
| 35 | 2.96M |     CSHA256 hasher; | 
| 36 | 2.96M |     hasher.Write((unsigned char*)&(*stream.begin()), stream.end() - stream.begin()); | 
| 37 | 2.96M |     uint256 shorttxidhash; | 
| 38 | 2.96M |     hasher.Finalize(shorttxidhash.begin()); | 
| 39 | 2.96M |     shorttxidk0 = shorttxidhash.GetUint64(0); | 
| 40 | 2.96M |     shorttxidk1 = shorttxidhash.GetUint64(1); | 
| 41 | 2.96M | } | 
| 42 |  |  | 
| 43 | 12.9M | uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const Wtxid& wtxid) const { | 
| 44 | 12.9M |     static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids calculation assumes 6-byte shorttxids"); | 
| 45 | 12.9M |     return SipHashUint256(shorttxidk0, shorttxidk1, wtxid.ToUint256()) & 0xffffffffffffL; | 
| 46 | 12.9M | } | 
| 47 |  |  | 
| 48 |  | /* Reconstructing a compact block is in the hot-path for block relay, | 
| 49 |  |  * so we want to do it as quickly as possible. Because this often | 
| 50 |  |  * involves iterating over the entire mempool, we put all the data we | 
| 51 |  |  * need (ie the wtxid and a reference to the actual transaction data) | 
| 52 |  |  * in a vector and iterate over the vector directly. This allows optimal | 
| 53 |  |  * CPU caching behaviour, at a cost of only 40 bytes per transaction. | 
| 54 |  |  */ | 
| 55 |  | ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<Wtxid, CTransactionRef>>& extra_txn) | 
| 56 | 149k | { | 
| 57 | 149k |     LogDebug(BCLog::CMPCTBLOCK, "Initializing PartiallyDownloadedBlock for block %s using a cmpctblock of %u bytes\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock)); | Line | Count | Source |  | 381 | 149k | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 149k |     do {                                                              \ |  | 374 | 149k |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 149k |     } while (0) | 
 | 
 | 
| 58 | 149k |     if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty()11.3k)) | 
| 59 | 0 |         return READ_STATUS_INVALID; | 
| 60 | 149k |     if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() > MAX_BLOCK_WEIGHT / MIN_SERIALIZABLE_TRANSACTION_WEIGHT) | 
| 61 | 0 |         return READ_STATUS_INVALID; | 
| 62 |  |  | 
| 63 | 149k |     if (!header.IsNull() || !txn_available.empty()) return READ_STATUS_INVALID0; | 
| 64 |  |  | 
| 65 | 149k |     header = cmpctblock.header; | 
| 66 | 149k |     txn_available.resize(cmpctblock.BlockTxCount()); | 
| 67 |  |  | 
| 68 | 149k |     int32_t lastprefilledindex = -1; | 
| 69 | 342k |     for (size_t i = 0; i < cmpctblock.prefilledtxn.size(); i++193k) { | 
| 70 | 193k |         if (cmpctblock.prefilledtxn[i].tx->IsNull()) | 
| 71 | 0 |             return READ_STATUS_INVALID; | 
| 72 |  |  | 
| 73 | 193k |         lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1; //index is a uint16_t, so can't overflow here | 
| 74 | 193k |         if (lastprefilledindex > std::numeric_limits<uint16_t>::max()) | 
| 75 | 0 |             return READ_STATUS_INVALID; | 
| 76 | 193k |         if ((uint32_t)lastprefilledindex > cmpctblock.shorttxids.size() + i) { | 
| 77 |  |             // If we are inserting a tx at an index greater than our full list of shorttxids | 
| 78 |  |             // plus the number of prefilled txn we've inserted, then we have txn for which we | 
| 79 |  |             // have neither a prefilled txn or a shorttxid! | 
| 80 | 0 |             return READ_STATUS_INVALID; | 
| 81 | 0 |         } | 
| 82 | 193k |         txn_available[lastprefilledindex] = cmpctblock.prefilledtxn[i].tx; | 
| 83 | 193k |     } | 
| 84 | 149k |     prefilled_count = cmpctblock.prefilledtxn.size(); | 
| 85 |  |  | 
| 86 |  |     // Calculate map of txids -> positions and check mempool to see what we have (or don't) | 
| 87 |  |     // Because well-formed cmpctblock messages will have a (relatively) uniform distribution | 
| 88 |  |     // of short IDs, any highly-uneven distribution of elements can be safely treated as a | 
| 89 |  |     // READ_STATUS_FAILED. | 
| 90 | 149k |     std::unordered_map<uint64_t, uint16_t> shorttxids(cmpctblock.shorttxids.size()); | 
| 91 | 149k |     uint16_t index_offset = 0; | 
| 92 | 1.47M |     for (size_t i = 0; i < cmpctblock.shorttxids.size(); i++1.32M) { | 
| 93 | 1.46M |         while (txn_available[i + index_offset]) | 
| 94 | 140k |             index_offset++; | 
| 95 | 1.32M |         shorttxids[cmpctblock.shorttxids[i]] = i + index_offset; | 
| 96 |  |         // To determine the chance that the number of entries in a bucket exceeds N, | 
| 97 |  |         // we use the fact that the number of elements in a single bucket is | 
| 98 |  |         // binomially distributed (with n = the number of shorttxids S, and p = | 
| 99 |  |         // 1 / the number of buckets), that in the worst case the number of buckets is | 
| 100 |  |         // equal to S (due to std::unordered_map having a default load factor of 1.0), | 
| 101 |  |         // and that the chance for any bucket to exceed N elements is at most | 
| 102 |  |         // buckets * (the chance that any given bucket is above N elements). | 
| 103 |  |         // Thus: P(max_elements_per_bucket > N) <= S * (1 - cdf(binomial(n=S,p=1/S), N)). | 
| 104 |  |         // If we assume blocks of up to 16000, allowing 12 elements per bucket should | 
| 105 |  |         // only fail once per ~1 million block transfers (per peer and connection). | 
| 106 | 1.32M |         if (shorttxids.bucket_size(shorttxids.bucket(cmpctblock.shorttxids[i])) > 12) | 
| 107 | 0 |             return READ_STATUS_FAILED; | 
| 108 | 1.32M |     } | 
| 109 |  |     // TODO: in the shortid-collision case, we should instead request both transactions | 
| 110 |  |     // which collided. Falling back to full-block-request here is overkill. | 
| 111 | 149k |     if (shorttxids.size() != cmpctblock.shorttxids.size()) | 
| 112 | 59.3k |         return READ_STATUS_FAILED; // Short ID collision | 
| 113 |  |  | 
| 114 | 89.7k |     std::vector<bool> have_txn(txn_available.size()); | 
| 115 | 89.7k |     { | 
| 116 | 89.7k |     LOCK(pool->cs); | Line | Count | Source |  | 259 | 89.7k | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 89.7k | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 89.7k | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 89.7k | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 117 | 223k |     for (const auto& [wtxid, txit] : pool->txns_randomized) { | 
| 118 | 223k |         uint64_t shortid = cmpctblock.GetShortID(wtxid); | 
| 119 | 223k |         std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid); | 
| 120 | 223k |         if (idit != shorttxids.end()) { | 
| 121 | 137k |             if (!have_txn[idit->second]) { | 
| 122 | 137k |                 txn_available[idit->second] = txit->GetSharedTx(); | 
| 123 | 137k |                 have_txn[idit->second]  = true; | 
| 124 | 137k |                 mempool_count++; | 
| 125 | 137k |             } else { | 
| 126 |  |                 // If we find two mempool txn that match the short id, just request it. | 
| 127 |  |                 // This should be rare enough that the extra bandwidth doesn't matter, | 
| 128 |  |                 // but eating a round-trip due to FillBlock failure would be annoying | 
| 129 | 0 |                 if (txn_available[idit->second]) { | 
| 130 | 0 |                     txn_available[idit->second].reset(); | 
| 131 | 0 |                     mempool_count--; | 
| 132 | 0 |                 } | 
| 133 | 0 |             } | 
| 134 | 137k |         } | 
| 135 |  |         // Though ideally we'd continue scanning for the two-txn-match-shortid case, | 
| 136 |  |         // the performance win of an early exit here is too good to pass up and worth | 
| 137 |  |         // the extra risk. | 
| 138 | 223k |         if (mempool_count == shorttxids.size()) | 
| 139 | 20.7k |             break; | 
| 140 | 223k |     } | 
| 141 | 89.7k |     } | 
| 142 |  |  | 
| 143 | 980k |     for (size_t i = 0; i < extra_txn.size(); i++890k) { | 
| 144 | 910k |         uint64_t shortid = cmpctblock.GetShortID(extra_txn[i].first); | 
| 145 | 910k |         std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid); | 
| 146 | 910k |         if (idit != shorttxids.end()) { | 
| 147 | 2.91k |             if (!have_txn[idit->second]) { | 
| 148 | 1.66k |                 txn_available[idit->second] = extra_txn[i].second; | 
| 149 | 1.66k |                 have_txn[idit->second]  = true; | 
| 150 | 1.66k |                 mempool_count++; | 
| 151 | 1.66k |                 extra_count++; | 
| 152 | 1.66k |             } else { | 
| 153 |  |                 // If we find two mempool/extra txn that match the short id, just | 
| 154 |  |                 // request it. | 
| 155 |  |                 // This should be rare enough that the extra bandwidth doesn't matter, | 
| 156 |  |                 // but eating a round-trip due to FillBlock failure would be annoying | 
| 157 |  |                 // Note that we don't want duplication between extra_txn and mempool to | 
| 158 |  |                 // trigger this case, so we compare witness hashes first | 
| 159 | 1.25k |                 if (txn_available[idit->second] && | 
| 160 | 1.25k |                         txn_available[idit->second]->GetWitnessHash() != extra_txn[i].second->GetWitnessHash()) { | 
| 161 | 0 |                     txn_available[idit->second].reset(); | 
| 162 | 0 |                     mempool_count--; | 
| 163 | 0 |                     extra_count--; | 
| 164 | 0 |                 } | 
| 165 | 1.25k |             } | 
| 166 | 2.91k |         } | 
| 167 |  |         // Though ideally we'd continue scanning for the two-txn-match-shortid case, | 
| 168 |  |         // the performance win of an early exit here is too good to pass up and worth | 
| 169 |  |         // the extra risk. | 
| 170 | 910k |         if (mempool_count == shorttxids.size()) | 
| 171 | 20.5k |             break; | 
| 172 | 910k |     } | 
| 173 |  |  | 
| 174 | 89.7k |     LogDebug(BCLog::CMPCTBLOCK, "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of %u bytes\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock)); | Line | Count | Source |  | 381 | 89.7k | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 89.7k |     do {                                                              \ |  | 374 | 89.7k |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 89.7k |     } while (0) | 
 | 
 | 
| 175 |  |  | 
| 176 | 89.7k |     return READ_STATUS_OK; | 
| 177 | 149k | } | 
| 178 |  |  | 
| 179 |  | bool PartiallyDownloadedBlock::IsTxAvailable(size_t index) const | 
| 180 | 622k | { | 
| 181 | 622k |     if (header.IsNull()) return false0; | 
| 182 |  |  | 
| 183 | 622k |     assert(index < txn_available.size()); | 
| 184 | 622k |     return txn_available[index] != nullptr; | 
| 185 | 622k | } | 
| 186 |  |  | 
| 187 |  | ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing, bool segwit_active) | 
| 188 | 31.4k | { | 
| 189 | 31.4k |     if (header.IsNull()) return READ_STATUS_INVALID0; | 
| 190 |  |  | 
| 191 | 31.4k |     uint256 hash = header.GetHash(); | 
| 192 | 31.4k |     block = header; | 
| 193 | 31.4k |     block.vtx.resize(txn_available.size()); | 
| 194 |  |  | 
| 195 | 31.4k |     unsigned int tx_missing_size = 0; | 
| 196 | 31.4k |     size_t tx_missing_offset = 0; | 
| 197 | 210k |     for (size_t i = 0; i < txn_available.size(); i++179k) { | 
| 198 | 181k |         if (!txn_available[i]) { | 
| 199 | 2.86k |             if (vtx_missing.size() <= tx_missing_offset) | 
| 200 | 1.78k |                 return READ_STATUS_INVALID; | 
| 201 | 1.07k |             block.vtx[i] = vtx_missing[tx_missing_offset++]; | 
| 202 | 1.07k |             tx_missing_size += block.vtx[i]->GetTotalSize(); | 
| 203 | 1.07k |         } else | 
| 204 | 178k |             block.vtx[i] = std::move(txn_available[i]); | 
| 205 | 181k |     } | 
| 206 |  |  | 
| 207 |  |     // Make sure we can't call FillBlock again. | 
| 208 | 29.6k |     header.SetNull(); | 
| 209 | 29.6k |     txn_available.clear(); | 
| 210 |  |  | 
| 211 | 29.6k |     if (vtx_missing.size() != tx_missing_offset) | 
| 212 | 12 |         return READ_STATUS_INVALID; | 
| 213 |  |  | 
| 214 |  |     // Check for possible mutations early now that we have a seemingly good block | 
| 215 | 29.6k |     IsBlockMutatedFn check_mutated{m_check_block_mutated_mock ? m_check_block_mutated_mock0: IsBlockMutated}; | 
| 216 | 29.6k |     if (check_mutated(/*block=*/block, | 
| 217 | 29.6k |                        /*check_witness_root=*/segwit_active)) { | 
| 218 | 454 |         return READ_STATUS_FAILED; // Possible Short ID collision | 
| 219 | 454 |     } | 
| 220 |  |  | 
| 221 | 29.1k |     LogDebug(BCLog::CMPCTBLOCK, "Successfully reconstructed block %s with %u txn prefilled, %u txn from mempool (incl at least %u from extra pool) and %u txn (%u bytes) requested\n", hash.ToString(), prefilled_count, mempool_count, extra_count, vtx_missing.size(), tx_missing_size); | Line | Count | Source |  | 381 | 29.1k | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 29.1k |     do {                                                              \ |  | 374 | 29.1k |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 29.1k |     } while (0) | 
 | 
 | 
| 222 | 29.1k |     if (vtx_missing.size() < 5) { | 
| 223 | 29.1k |         for (const auto& tx : vtx_missing) { | 
| 224 | 309 |             LogDebug(BCLog::CMPCTBLOCK, "Reconstructed block %s required tx %s\n", hash.ToString(), tx->GetHash().ToString()); | Line | Count | Source |  | 381 | 309 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 309 |     do {                                                              \ |  | 374 | 309 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 309 |     } while (0) | 
 | 
 | 
| 225 | 309 |         } | 
| 226 | 29.1k |     } | 
| 227 |  |  | 
| 228 | 29.1k |     return READ_STATUS_OK; | 
| 229 | 29.6k | } |