Bitcoin Core 32.99.0
P2P Digital Currency
Loading...
Searching...
No Matches
blockencodings.cpp
Go to the documentation of this file.
1// Copyright (c) 2016-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 <blockencodings.h>
6#include <chainparams.h>
7#include <common/system.h>
10#include <crypto/sha256.h>
11#include <crypto/siphash.h>
12#include <logging/categories.h>
13#include <random.h>
14#include <streams.h>
15#include <txmempool.h>
16#include <util/log.h>
17#include <validation.h>
18
19#include <unordered_map>
20
22 : nonce(nonce),
23 shorttxids(block.vtx.size() - 1),
24 prefilledtxn(1),
25 header(block)
26{
28 // TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase
29 prefilledtxn[0] = {0, block.vtx[0]};
30 for (size_t i = 1; i < block.vtx.size(); i++) {
31 const CTransaction& tx = *block.vtx[i];
33 }
34}
35
37{
39 stream << header << nonce;
41 hasher.Write((unsigned char*)&(*stream.begin()), stream.end() - stream.begin());
43 hasher.Finalize(shorttxidhash.begin());
44 m_hasher.emplace(shorttxidhash.GetUint64(0), shorttxidhash.GetUint64(1));
45}
46
48{
49 static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids calculation assumes 6-byte shorttxids");
50 return (*Assert(m_hasher))(wtxid.ToUint256()) & 0xffffffffffffL;
51}
52
53/* Reconstructing a compact block is in the hot-path for block relay,
54 * so we want to do it as quickly as possible. Because this often
55 * involves iterating over the entire mempool, we put all the data we
56 * need (ie the wtxid and a reference to the actual transaction data)
57 * in a vector and iterate over the vector directly. This allows optimal
58 * CPU caching behaviour, at a cost of only 40 bytes per transaction.
59 */
60ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<Wtxid, CTransactionRef>>& extra_txn)
61{
62 LogDebug(BCLog::CMPCTBLOCK, "Initializing PartiallyDownloadedBlock for block %s using a cmpctblock of %u bytes\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock));
63 if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty()))
65 if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() > MAX_BLOCK_WEIGHT / MIN_SERIALIZABLE_TRANSACTION_WEIGHT)
67
68 if (!header.IsNull() || !txn_available.empty()) return READ_STATUS_INVALID;
69
70 header = cmpctblock.header;
71 txn_available.resize(cmpctblock.BlockTxCount());
72
74 // Track the prefills which were not already present in the mempool or extrapool.
75 std::map<Wtxid, size_t> leftover_prefills;
76
78 prefilled_count = cmpctblock.prefilledtxn.size();
79 for (size_t i = 0; i < prefilled_count; i++) {
80 if (cmpctblock.prefilledtxn[i].tx->IsNull())
82
83 lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1; //index is a uint16_t, so can't overflow here
84 if (lastprefilledindex > std::numeric_limits<uint16_t>::max())
86 if ((uint32_t)lastprefilledindex > cmpctblock.shorttxids.size() + i) {
87 // If we are inserting a tx at an index greater than our full list of shorttxids
88 // plus the number of prefilled txn we've inserted, then we have txn for which we
89 // have neither a prefilled txn or a shorttxid!
91 }
92
93 if (debug_log) {
94 const CTransactionRef& tx{cmpctblock.prefilledtxn[i].tx};
95 const size_t tx_size{tx->ComputeTotalSize()};
97
98 leftover_prefills.emplace(tx->GetWitnessHash(), tx_size);
99 }
100 txn_available[lastprefilledindex] = cmpctblock.prefilledtxn[i].tx;
101 }
102
103
104 // Calculate map of txids -> positions and check mempool to see what we have (or don't)
105 // Because well-formed cmpctblock messages will have a (relatively) uniform distribution
106 // of short IDs, any highly-uneven distribution of elements can be safely treated as a
107 // READ_STATUS_FAILED.
108 std::unordered_map<uint64_t, uint16_t> shorttxids(cmpctblock.shorttxids.size());
110 for (size_t i = 0; i < cmpctblock.shorttxids.size(); i++) {
111 while (txn_available[i + index_offset])
112 index_offset++;
113 shorttxids[cmpctblock.shorttxids[i]] = i + index_offset;
114 // To determine the chance that the number of entries in a bucket exceeds N,
115 // we use the fact that the number of elements in a single bucket is
116 // binomially distributed (with n = the number of shorttxids S, and p =
117 // 1 / the number of buckets), that in the worst case the number of buckets is
118 // equal to S (due to std::unordered_map having a default load factor of 1.0),
119 // and that the chance for any bucket to exceed N elements is at most
120 // buckets * (the chance that any given bucket is above N elements).
121 // Thus: P(max_elements_per_bucket > N) <= S * (1 - cdf(binomial(n=S,p=1/S), N)).
122 // If we assume blocks of up to 16000, allowing 12 elements per bucket should
123 // only fail once per ~1 million block transfers (per peer and connection).
124 if (shorttxids.bucket_size(shorttxids.bucket(cmpctblock.shorttxids[i])) > 12)
125 return READ_STATUS_FAILED;
126 }
127 if (shorttxids.size() != cmpctblock.shorttxids.size())
128 return READ_STATUS_FAILED; // Short ID collision
129
130 enum class TxSource : uint8_t { NONE, MEMPOOL, EXTRA, COLLIDED };
131 std::vector<TxSource> tx_source(txn_available.size(), TxSource::NONE);
132 size_t available_count = 0;
133 {
134 LOCK(pool->cs);
135 if (debug_log) {
136 for (auto it{leftover_prefills.begin()}; it != leftover_prefills.end();) {
137 if (pool->GetIter(it->first)) {
140 it = leftover_prefills.erase(it);
141 } else {
142 ++it;
143 }
144 }
145 }
146
147 for (const auto& [wtxid, txit] : pool->txns_randomized) {
148 uint64_t shortid = cmpctblock.GetShortID(wtxid);
149 std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
150 if (idit != shorttxids.end()) {
151 if (tx_source[idit->second] == TxSource::NONE) {
152 txn_available[idit->second] = txit->GetSharedTx();
153 tx_source[idit->second] = TxSource::MEMPOOL;
155 } else if (tx_source[idit->second] != TxSource::COLLIDED) {
156 // If we find two mempool txn that match the short id, just request it.
157 // This should be rare enough that the extra bandwidth doesn't matter,
158 // but eating a round-trip due to FillBlock failure would be annoying
159 txn_available[idit->second].reset();
161 tx_source[idit->second] = TxSource::COLLIDED;
162 }
163 }
164 // Though ideally we'd continue scanning for the two-txn-match-shortid case,
165 // the performance win of an early exit here is too good to pass up and worth
166 // the extra risk.
167 if (available_count == shorttxids.size())
168 break;
169 }
170 }
171
172 for (size_t i = 0; i < extra_txn.size(); i++) {
173 uint64_t shortid = cmpctblock.GetShortID(extra_txn[i].first);
174 std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
175 if (idit != shorttxids.end()) {
176 if (tx_source[idit->second] == TxSource::NONE) {
177 txn_available[idit->second] = extra_txn[i].second;
178 tx_source[idit->second] = TxSource::EXTRA;
180 } else if (tx_source[idit->second] != TxSource::COLLIDED &&
181 txn_available[idit->second]->GetWitnessHash() != extra_txn[i].second->GetWitnessHash()) {
182 // If we find two mempool/extra txn that match the short id, just
183 // request it.
184 // This should be rare enough that the extra bandwidth doesn't matter,
185 // but eating a round-trip due to FillBlock failure would be annoying
186 // Note that we don't want duplication between extra_txn and mempool to
187 // trigger this case, so we compare witness hashes first
188 txn_available[idit->second].reset();
190 tx_source[idit->second] = TxSource::COLLIDED;
191 }
192 }
193 // Though ideally we'd continue scanning for the two-txn-match-shortid case,
194 // the performance win of an early exit here is too good to pass up and worth
195 // the extra risk.
196 if (available_count == shorttxids.size())
197 break;
198 }
199
200
201 if (debug_log) {
202 for (const auto& [id, tx] : extra_txn) {
203 if (!tx) continue;
204 const auto it{leftover_prefills.find(id)};
205 if (it == leftover_prefills.end()) continue;
208 leftover_prefills.erase(it);
209 }
210
211 Assume(txn_available.size() == tx_source.size());
212 for (size_t i = 0; i < txn_available.size(); i++) {
213 switch (tx_source[i]) {
214 case TxSource::MEMPOOL:
217 mempool_size += txn_available[i]->ComputeTotalSize();
218 break;
219 case TxSource::EXTRA:
220 ++extra_count;
222 extra_size += txn_available[i]->ComputeTotalSize();
223 break;
224 default:
225 break;
226 }
227 }
228 }
229
230 LogDebug(BCLog::CMPCTBLOCK, "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of %u bytes\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock));
231
232 return READ_STATUS_OK;
233}
234
236{
237 if (header.IsNull()) return false;
238
239 assert(index < txn_available.size());
240 return txn_available[index] != nullptr;
241}
242
243ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing, bool segwit_active)
244{
245 if (header.IsNull()) return READ_STATUS_INVALID;
246
247 block = header;
248 block.vtx.resize(txn_available.size());
249
250 size_t tx_missing_offset = 0;
251 for (size_t i = 0; i < txn_available.size(); i++) {
252 if (!txn_available[i]) {
253 if (tx_missing_offset >= vtx_missing.size()) {
254 return READ_STATUS_INVALID;
255 }
256 block.vtx[i] = vtx_missing[tx_missing_offset++];
257 } else {
258 block.vtx[i] = std::move(txn_available[i]);
259 }
260 }
261
262 // Make sure we can't call FillBlock again.
263 header.SetNull();
264 txn_available.clear();
265
266 if (vtx_missing.size() != tx_missing_offset) {
267 return READ_STATUS_INVALID;
268 }
269
270 // Check for possible mutations early now that we have a seemingly good block
272 if (check_mutated(/*block=*/block, /*check_witness_root=*/segwit_active)) {
273 return READ_STATUS_FAILED; // Possible Short ID collision
274 }
275
277 const uint256 hash{block.GetHash()};
279 for (const auto& tx : vtx_missing) { tx_missing_size += tx->ComputeTotalSize(); }
281 "Successfully reconstructed block %s with %u txn prefilled (%u bytes), "
282 "%u txn from mempool (%u bytes), "
283 "%u txn from extrapool (%u bytes), "
284 "and %u txn requested (%u bytes)",
285 hash.ToString(),
291 "Block %s prefill redundancy statistics: "
292 "%u txn (%u bytes) of the prefill were redundant, "
293 "%u txn (%u bytes) were present in the mempool, "
294 "%u txn (%u bytes) were present in the extrapool.",
295 hash.ToString(),
300
302 const std::string missing_txids{util::Join(vtx_missing, ", ", [](const auto& tx) { return tx->GetHash().ToString(); })};
303 LogTrace(BCLog::CMPCTBLOCK, "Reconstructed block %s required requesting the following transactions: %s\n", hash.ToString(), missing_txids);
304 }
305 }
306
307 return READ_STATUS_OK;
308}
@ READ_STATUS_OK
@ READ_STATUS_INVALID
@ READ_STATUS_FAILED
enum ReadStatus_t ReadStatus
#define Assert(val)
Identity function.
Definition check.h:119
#define Assume(val)
Assume is the identity function.
Definition check.h:131
CBlockHeaderAndShortTxIDs()=default
Dummy for deserialization.
uint64_t GetShortID(const Wtxid &wtxid) const
std::vector< PrefilledTransaction > prefilledtxn
static constexpr int SHORTTXIDS_LENGTH
std::vector< uint64_t > shorttxids
std::optional< PresaltedSipHasher > m_hasher
void SetNull()
Definition block.h:44
uint256 GetHash() const
Definition block.cpp:14
bool IsNull() const
Definition block.h:54
Definition block.h:74
std::vector< CTransactionRef > vtx
Definition block.h:77
A hasher class for SHA-256.
Definition sha256.h:14
CSHA256 & Write(const unsigned char *data, size_t len)
Definition sha256.cpp:699
The basic transaction that is broadcasted on the network and contained in blocks.
const Wtxid & GetWitnessHash() const LIFETIMEBOUND
std::optional< txiter > GetIter(const Txid &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Returns an iterator to the given hash, if found.
Double ended buffer combining vector and stream-like interfaces.
Definition streams.h:165
const CTxMemPool * pool
IsBlockMutatedFn m_check_block_mutated_mock
std::vector< CTransactionRef > txn_available
ReadStatus FillBlock(CBlock &block, const std::vector< CTransactionRef > &vtx_missing, bool segwit_active)
std::function< bool(const CBlock &, bool)> IsBlockMutatedFn
bool IsTxAvailable(size_t index) const
ReadStatus InitData(const CBlockHeaderAndShortTxIDs &cmpctblock, const std::vector< std::pair< Wtxid, CTransactionRef > > &extra_txn)
256-bit opaque blob.
Definition uint256.h:196
constexpr size_t MIN_SERIALIZABLE_TRANSACTION_WEIGHT
Definition consensus.h:24
constexpr unsigned int MAX_BLOCK_WEIGHT
The maximum allowed weight for a block, see BIP 141 (network rule)
Definition consensus.h:15
#define LogTrace(category,...)
Definition log.h:155
#define LogDebug(category,...)
Definition log.h:154
unsigned int nonce
@ CMPCTBLOCK
Definition categories.h:28
bool ShouldDebugLog(Category)
Return whether messages with specified category should be debug logged.
bool ShouldTraceLog(Category)
Return whether messages with specified category should be trace logged.
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition string.h:209
std::shared_ptr< const CTransaction > CTransactionRef
uint64_t GetSerializeSize(const T &t)
Definition serialize.h:1157
#define LOCK(cs)
Definition sync.h:268
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:82
bool IsBlockMutated(const CBlock &block, bool check_witness_root)
Check if a block has been mutated (with respect to its merkle root and witness commitments).
assert(!tx.IsCoinBase())