Skip to content

Commit 6f17f5b

Browse files
Merge bitcoin#32646: p2p: Add witness mutation check inside FillBlock
Backport of bitcoin#32646 (commits bac9ee4, 28299ce), slimmed for Dash. PartiallyDownloadedBlock::FillBlock now calls IsBlockMutated() instead of CheckBlock() as a belt-and-suspenders mutation check on reconstructed compact blocks, and the vestigial READ_STATUS_CHECKBLOCK_FAILED status is removed. Dash adaptations: no segwit, so FillBlock keeps its 2-arg signature (no segwit_active parameter) and the mutation mock hook becomes IsBlockMutatedFn = std::function<bool(const CBlock&)> (m_check_block_mutated_mock). The fuzz harness is updated to mock IsBlockMutated instead of CheckBlock.
1 parent f43c2b8 commit 6f17f5b

4 files changed

Lines changed: 15 additions & 60 deletions

File tree

src/blockencodings.cpp

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,16 +203,10 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
203203
if (vtx_missing.size() != tx_missing_offset)
204204
return READ_STATUS_INVALID;
205205

206-
BlockValidationState state;
207-
CheckBlockFn check_block = m_check_block_mock ? m_check_block_mock : CheckBlock;
208-
if (!check_block(block, state, Params().GetConsensus(), /*fCheckPoW=*/true, /*fCheckMerkleRoot=*/true)) {
209-
// TODO: We really want to just check merkle tree manually here,
210-
// but that is expensive, and CheckBlock caches a block's
211-
// "checked-status" (in the CBlock?). CBlock should be able to
212-
// check its own merkle root and cache that check.
213-
if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED)
214-
return READ_STATUS_FAILED; // Possible Short ID collision
215-
return READ_STATUS_CHECKBLOCK_FAILED;
206+
// Check for possible mutations early now that we have a seemingly good block
207+
IsBlockMutatedFn check_mutated{m_check_block_mutated_mock ? m_check_block_mutated_mock : IsBlockMutated};
208+
if (check_mutated(/*block=*/block)) {
209+
return READ_STATUS_FAILED; // Possible Short ID collision
216210
}
217211

218212
LogPrint(BCLog::CMPCTBLOCK, "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool (incl at least %lu from extra pool) and %lu txn requested\n", hash.ToString(), prefilled_count, mempool_count, extra_count, vtx_missing.size());

src/blockencodings.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,6 @@ typedef enum ReadStatus_t
8484
READ_STATUS_OK,
8585
READ_STATUS_INVALID, // Invalid object, peer is sending bogus crap
8686
READ_STATUS_FAILED, // Failed to process object
87-
READ_STATUS_CHECKBLOCK_FAILED, // Used only by FillBlock to indicate a
88-
// failure in CheckBlock.
8987
} ReadStatus;
9088

9189
class CBlockHeaderAndShortTxIDs {
@@ -136,8 +134,8 @@ class PartiallyDownloadedBlock {
136134
CBlockHeader header;
137135

138136
// Can be overriden for testing
139-
using CheckBlockFn = std::function<bool(const CBlock&, BlockValidationState&, const Consensus::Params&, bool, bool)>;
140-
CheckBlockFn m_check_block_mock{nullptr};
137+
using IsBlockMutatedFn = std::function<bool(const CBlock&)>;
138+
IsBlockMutatedFn m_check_block_mutated_mock{nullptr};
141139

142140
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
143141

src/net_processing.cpp

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3674,23 +3674,7 @@ void PeerManagerImpl::ProcessCompactBlockTxns(CNode& pfrom, Peer& peer, const Bl
36743674
return;
36753675
}
36763676
} else {
3677-
// Block is either okay, or possibly we received
3678-
// READ_STATUS_CHECKBLOCK_FAILED.
3679-
// Note that CheckBlock can only fail for one of a few reasons:
3680-
// 1. bad-proof-of-work (impossible here, because we've already
3681-
// accepted the header)
3682-
// 2. merkleroot doesn't match the transactions given (already
3683-
// caught in FillBlock with READ_STATUS_FAILED, so
3684-
// impossible here)
3685-
// 3. the block is otherwise invalid (eg invalid coinbase,
3686-
// block is too big, too many legacy sigops, etc).
3687-
// So if CheckBlock failed, #3 is the only possibility.
3688-
// Under BIP 152, we don't discourage the peer unless proof of work is
3689-
// invalid (we don't require all the stateless checks to have
3690-
// been run). This is handled below, so just treat this as
3691-
// though the block was successfully read, and rely on the
3692-
// handling in ProcessNewBlock to ensure the block index is
3693-
// updated, etc.
3677+
// Block is okay for further processing
36943678
RemoveBlockRequest(block_transactions.blockhash, pfrom.GetId()); // it is now an empty pointer
36953679
fBlockRead = true;
36963680
// mapBlockSource is used for potentially punishing peers and

src/test/fuzz/partially_downloaded_block.cpp

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,10 @@ void initialize_pdb()
2828
g_setup = testing_setup.get();
2929
}
3030

31-
PartiallyDownloadedBlock::CheckBlockFn FuzzedCheckBlock(std::optional<BlockValidationResult> result)
31+
PartiallyDownloadedBlock::IsBlockMutatedFn FuzzedIsBlockMutated(bool result)
3232
{
33-
return [result](const CBlock&, BlockValidationState& state, const Consensus::Params&, bool, bool) {
34-
if (result) {
35-
return state.Invalid(*result);
36-
}
37-
38-
return true;
33+
return [result](const CBlock& block) {
34+
return result;
3935
};
4036
}
4137

@@ -103,37 +99,20 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
10399
skipped_missing |= (!pdb.IsTxAvailable(i) && skip);
104100
}
105101

106-
// Mock CheckBlock
107-
bool fail_check_block{fuzzed_data_provider.ConsumeBool()};
108-
auto validation_result =
109-
fuzzed_data_provider.PickValueInArray(
110-
{BlockValidationResult::BLOCK_RESULT_UNSET,
111-
BlockValidationResult::BLOCK_CONSENSUS,
112-
BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE,
113-
BlockValidationResult::BLOCK_CACHED_INVALID,
114-
BlockValidationResult::BLOCK_INVALID_HEADER,
115-
BlockValidationResult::BLOCK_MUTATED,
116-
BlockValidationResult::BLOCK_MISSING_PREV,
117-
BlockValidationResult::BLOCK_INVALID_PREV,
118-
BlockValidationResult::BLOCK_TIME_FUTURE,
119-
BlockValidationResult::BLOCK_CHECKPOINT,
120-
BlockValidationResult::BLOCK_CHAINLOCK});
121-
pdb.m_check_block_mock = FuzzedCheckBlock(
122-
fail_check_block ?
123-
std::optional<BlockValidationResult>{validation_result} :
124-
std::nullopt);
102+
// Mock IsBlockMutated
103+
bool fail_block_mutated{fuzzed_data_provider.ConsumeBool()};
104+
pdb.m_check_block_mutated_mock = FuzzedIsBlockMutated(fail_block_mutated);
125105

126106
CBlock reconstructed_block;
127107
auto fill_status{pdb.FillBlock(reconstructed_block, missing)};
128108
switch (fill_status) {
129109
case READ_STATUS_OK:
130110
assert(!skipped_missing);
131-
assert(!fail_check_block);
111+
assert(!fail_block_mutated);
132112
assert(block->GetHash() == reconstructed_block.GetHash());
133113
break;
134-
case READ_STATUS_CHECKBLOCK_FAILED: [[fallthrough]];
135114
case READ_STATUS_FAILED:
136-
assert(fail_check_block);
115+
assert(fail_block_mutated);
137116
break;
138117
case READ_STATUS_INVALID:
139118
break;

0 commit comments

Comments
 (0)