Skip to content

Commit 50a664a

Browse files
committed
Merge bitcoin/bitcoin#26969: net, refactor: net_processing, add ProcessCompactBlockTxns
77d6d89 net: net_processing, add `ProcessCompactBlockTxns` (brunoerg) Pull request description: When processing `CMPCTBLOCK` message, at some moments we can need to process compact block txns / `BLOCKTXN`, since all messages are handled by `ProcessMessage`, so we call `ProcessMessage` all over again. https://github.com/bitcoin/bitcoin/blob/ab98673f058853e00c310afad57925f54c1ecfae/src/net_processing.cpp#L4331-L4348 This PR creates a function called `ProcessCompactBlockTxns` to process it to avoid calling `ProcessMessage` for it - this function is also called when processing `BLOCKTXN` msg. ACKs for top commit: instagibbs: reACK 77d6d89 ajtowns: utACK 77d6d89 achow101: ACK 77d6d89 Tree-SHA512: 4b73c189487b999a04a8f15608a2ac1966d0f5c6db3ae0782641e68b9e95cb0807bd065d124c1f316b25b04d522a765addcd7d82c541702695113d4e54db4fda
2 parents 035ae61 + 77d6d89 commit 50a664a

File tree

1 file changed

+95
-92
lines changed

1 file changed

+95
-92
lines changed

src/net_processing.cpp

Lines changed: 95 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,10 @@ class PeerManagerImpl final : public PeerManager
918918
/** Process a new block. Perform any post-processing housekeeping */
919919
void ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing, bool min_pow_checked);
920920

921+
/** Process compact block txns */
922+
void ProcessCompactBlockTxns(CNode& pfrom, Peer& peer, const BlockTransactions& block_transactions)
923+
EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex, !m_most_recent_block_mutex);
924+
921925
/**
922926
* When a peer sends us a valid block, instruct it to announce blocks to us
923927
* using CMPCTBLOCK if possible by adding its nodeid to the end of
@@ -3204,6 +3208,93 @@ void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr<const CBlo
32043208
}
32053209
}
32063210

3211+
void PeerManagerImpl::ProcessCompactBlockTxns(CNode& pfrom, Peer& peer, const BlockTransactions& block_transactions)
3212+
{
3213+
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
3214+
bool fBlockRead{false};
3215+
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
3216+
{
3217+
LOCK(cs_main);
3218+
3219+
auto range_flight = mapBlocksInFlight.equal_range(block_transactions.blockhash);
3220+
size_t already_in_flight = std::distance(range_flight.first, range_flight.second);
3221+
bool requested_block_from_this_peer{false};
3222+
3223+
// Multimap ensures ordering of outstanding requests. It's either empty or first in line.
3224+
bool first_in_flight = already_in_flight == 0 || (range_flight.first->second.first == pfrom.GetId());
3225+
3226+
while (range_flight.first != range_flight.second) {
3227+
auto [node_id, block_it] = range_flight.first->second;
3228+
if (node_id == pfrom.GetId() && block_it->partialBlock) {
3229+
requested_block_from_this_peer = true;
3230+
break;
3231+
}
3232+
range_flight.first++;
3233+
}
3234+
3235+
if (!requested_block_from_this_peer) {
3236+
LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom.GetId());
3237+
return;
3238+
}
3239+
3240+
PartiallyDownloadedBlock& partialBlock = *range_flight.first->second.second->partialBlock;
3241+
ReadStatus status = partialBlock.FillBlock(*pblock, block_transactions.txn);
3242+
if (status == READ_STATUS_INVALID) {
3243+
RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId()); // Reset in-flight state in case Misbehaving does not result in a disconnect
3244+
Misbehaving(peer, 100, "invalid compact block/non-matching block transactions");
3245+
return;
3246+
} else if (status == READ_STATUS_FAILED) {
3247+
if (first_in_flight) {
3248+
// Might have collided, fall back to getdata now :(
3249+
std::vector<CInv> invs;
3250+
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(peer), block_transactions.blockhash));
3251+
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
3252+
} else {
3253+
RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId());
3254+
LogPrint(BCLog::NET, "Peer %d sent us a compact block but it failed to reconstruct, waiting on first download to complete\n", pfrom.GetId());
3255+
return;
3256+
}
3257+
} else {
3258+
// Block is either okay, or possibly we received
3259+
// READ_STATUS_CHECKBLOCK_FAILED.
3260+
// Note that CheckBlock can only fail for one of a few reasons:
3261+
// 1. bad-proof-of-work (impossible here, because we've already
3262+
// accepted the header)
3263+
// 2. merkleroot doesn't match the transactions given (already
3264+
// caught in FillBlock with READ_STATUS_FAILED, so
3265+
// impossible here)
3266+
// 3. the block is otherwise invalid (eg invalid coinbase,
3267+
// block is too big, too many legacy sigops, etc).
3268+
// So if CheckBlock failed, #3 is the only possibility.
3269+
// Under BIP 152, we don't discourage the peer unless proof of work is
3270+
// invalid (we don't require all the stateless checks to have
3271+
// been run). This is handled below, so just treat this as
3272+
// though the block was successfully read, and rely on the
3273+
// handling in ProcessNewBlock to ensure the block index is
3274+
// updated, etc.
3275+
RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId()); // it is now an empty pointer
3276+
fBlockRead = true;
3277+
// mapBlockSource is used for potentially punishing peers and
3278+
// updating which peers send us compact blocks, so the race
3279+
// between here and cs_main in ProcessNewBlock is fine.
3280+
// BIP 152 permits peers to relay compact blocks after validating
3281+
// the header only; we should not punish peers if the block turns
3282+
// out to be invalid.
3283+
mapBlockSource.emplace(block_transactions.blockhash, std::make_pair(pfrom.GetId(), false));
3284+
}
3285+
} // Don't hold cs_main when we call into ProcessNewBlock
3286+
if (fBlockRead) {
3287+
// Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
3288+
// even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
3289+
// This bypasses some anti-DoS logic in AcceptBlock (eg to prevent
3290+
// disk-space attacks), but this should be safe due to the
3291+
// protections in the compact block handler -- see related comment
3292+
// in compact block optimistic reconstruction handling.
3293+
ProcessBlock(pfrom, pblock, /*force_processing=*/true, /*min_pow_checked=*/true);
3294+
}
3295+
return;
3296+
}
3297+
32073298
void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
32083299
const std::chrono::microseconds time_received,
32093300
const std::atomic<bool>& interruptMsgProc)
@@ -4276,12 +4367,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
42764367
blockhash.ToString(), pfrom.GetId());
42774368
}
42784369

4279-
// When we succeed in decoding a block's txids from a cmpctblock
4280-
// message we typically jump to the BLOCKTXN handling code, with a
4281-
// dummy (empty) BLOCKTXN message, to re-use the logic there in
4282-
// completing processing of the putative block (without cs_main).
42834370
bool fProcessBLOCKTXN = false;
4284-
CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION);
42854371

42864372
// If we end up treating this as a plain headers message, call that as well
42874373
// without cs_main.
@@ -4382,10 +4468,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
43824468
req.indexes.push_back(i);
43834469
}
43844470
if (req.indexes.empty()) {
4385-
// Dirty hack to jump to BLOCKTXN code (TODO: move message handling into their own functions)
4386-
BlockTransactions txn;
4387-
txn.blockhash = blockhash;
4388-
blockTxnMsg << txn;
43894471
fProcessBLOCKTXN = true;
43904472
} else if (first_in_flight) {
43914473
// We will try to round-trip any compact blocks we get on failure,
@@ -4440,7 +4522,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
44404522
} // cs_main
44414523

44424524
if (fProcessBLOCKTXN) {
4443-
return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, interruptMsgProc);
4525+
BlockTransactions txn;
4526+
txn.blockhash = blockhash;
4527+
return ProcessCompactBlockTxns(pfrom, *peer, txn);
44444528
}
44454529

44464530
if (fRevertToHeaderProcessing) {
@@ -4492,88 +4576,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
44924576
BlockTransactions resp;
44934577
vRecv >> resp;
44944578

4495-
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
4496-
bool fBlockRead = false;
4497-
{
4498-
LOCK(cs_main);
4499-
4500-
auto range_flight = mapBlocksInFlight.equal_range(resp.blockhash);
4501-
size_t already_in_flight = std::distance(range_flight.first, range_flight.second);
4502-
bool requested_block_from_this_peer{false};
4503-
4504-
// Multimap ensures ordering of outstanding requests. It's either empty or first in line.
4505-
bool first_in_flight = already_in_flight == 0 || (range_flight.first->second.first == pfrom.GetId());
4506-
4507-
while (range_flight.first != range_flight.second) {
4508-
auto [node_id, block_it] = range_flight.first->second;
4509-
if (node_id == pfrom.GetId() && block_it->partialBlock) {
4510-
requested_block_from_this_peer = true;
4511-
break;
4512-
}
4513-
range_flight.first++;
4514-
}
4515-
4516-
if (!requested_block_from_this_peer) {
4517-
LogPrint(BCLog::NET, "Peer %d sent us block transactions for block we weren't expecting\n", pfrom.GetId());
4518-
return;
4519-
}
4520-
4521-
PartiallyDownloadedBlock& partialBlock = *range_flight.first->second.second->partialBlock;
4522-
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn);
4523-
if (status == READ_STATUS_INVALID) {
4524-
RemoveBlockRequest(resp.blockhash, pfrom.GetId()); // Reset in-flight state in case Misbehaving does not result in a disconnect
4525-
Misbehaving(*peer, 100, "invalid compact block/non-matching block transactions");
4526-
return;
4527-
} else if (status == READ_STATUS_FAILED) {
4528-
if (first_in_flight) {
4529-
// Might have collided, fall back to getdata now :(
4530-
std::vector<CInv> invs;
4531-
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(*peer), resp.blockhash));
4532-
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, invs));
4533-
} else {
4534-
RemoveBlockRequest(resp.blockhash, pfrom.GetId());
4535-
LogPrint(BCLog::NET, "Peer %d sent us a compact block but it failed to reconstruct, waiting on first download to complete\n", pfrom.GetId());
4536-
return;
4537-
}
4538-
} else {
4539-
// Block is either okay, or possibly we received
4540-
// READ_STATUS_CHECKBLOCK_FAILED.
4541-
// Note that CheckBlock can only fail for one of a few reasons:
4542-
// 1. bad-proof-of-work (impossible here, because we've already
4543-
// accepted the header)
4544-
// 2. merkleroot doesn't match the transactions given (already
4545-
// caught in FillBlock with READ_STATUS_FAILED, so
4546-
// impossible here)
4547-
// 3. the block is otherwise invalid (eg invalid coinbase,
4548-
// block is too big, too many legacy sigops, etc).
4549-
// So if CheckBlock failed, #3 is the only possibility.
4550-
// Under BIP 152, we don't discourage the peer unless proof of work is
4551-
// invalid (we don't require all the stateless checks to have
4552-
// been run). This is handled below, so just treat this as
4553-
// though the block was successfully read, and rely on the
4554-
// handling in ProcessNewBlock to ensure the block index is
4555-
// updated, etc.
4556-
RemoveBlockRequest(resp.blockhash, pfrom.GetId()); // it is now an empty pointer
4557-
fBlockRead = true;
4558-
// mapBlockSource is used for potentially punishing peers and
4559-
// updating which peers send us compact blocks, so the race
4560-
// between here and cs_main in ProcessNewBlock is fine.
4561-
// BIP 152 permits peers to relay compact blocks after validating
4562-
// the header only; we should not punish peers if the block turns
4563-
// out to be invalid.
4564-
mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom.GetId(), false));
4565-
}
4566-
} // Don't hold cs_main when we call into ProcessNewBlock
4567-
if (fBlockRead) {
4568-
// Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
4569-
// even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
4570-
// This bypasses some anti-DoS logic in AcceptBlock (eg to prevent
4571-
// disk-space attacks), but this should be safe due to the
4572-
// protections in the compact block handler -- see related comment
4573-
// in compact block optimistic reconstruction handling.
4574-
ProcessBlock(pfrom, pblock, /*force_processing=*/true, /*min_pow_checked=*/true);
4575-
}
4576-
return;
4579+
return ProcessCompactBlockTxns(pfrom, *peer, resp);
45774580
}
45784581

45794582
if (msg_type == NetMsgType::HEADERS)

0 commit comments

Comments
 (0)