aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
authorfanquake <fanquake@gmail.com>2024-03-22 13:39:23 +0000
committerfanquake <fanquake@gmail.com>2024-03-22 13:40:07 +0000
commitd5bad0d2d16c2ddd6bfaeccbca2a31a30227ba2c (patch)
tree983636b811b9d7824aa4f46296c9d25f8c238890 /src/validation.cpp
parent1ce5accc325cd7d3382f67a314dec018cfdc0a3e (diff)
parent27cfda1baec63ee4fb0f743576227528104fe495 (diff)
Merge bitcoin/bitcoin#29531: [25.x] backportsv25.2rc2
27cfda1baec63ee4fb0f743576227528104fe495 doc: Update release notes for 25.2rc2 (Ava Chow) daba5e2c5be67a1bcb9af2e65464d3b92b042460 doc: Update manpages for 25.2rc2 (Ava Chow) 8a0c980d6e8941cb55933f6bcb44bed500e1648e build: Bump to 25.2rc2 (Ava Chow) cf7d3a8cd07c26c700eee4bc1a16092982625326 p2p: Don't consider blocks mutated if they don't connect to known prev block (Greg Sanders) 3eaaafa225c489405d71d0de1daff8b403e60ef7 [test] IsBlockMutated unit tests (dergoegge) 0667441a7b34dde79fe0ecfc0cddc3314fa05f63 [validation] Cache merkle root and witness commitment checks (dergoegge) de97ecf14f2bd8cc42e8703ac028251ecd8e42d9 [test] Add regression test for #27608 (dergoegge) 8cc4b24c74ecf7a3c3d2853fe8ecb474eb77284c [net processing] Don't process mutated blocks (dergoegge) 098f07dc8d79f1bf55441e23b98d609e425d7d16 [validation] Merkle root malleation should be caught by IsBlockMutated (dergoegge) 8804c368f5b5745ae4e7bcbc60bae36658d7e2c4 [validation] Introduce IsBlockMutated (dergoegge) 4f5baac6ca46435ed7546e1577f9f6120aae5355 [validation] Isolate merkle root checks (dergoegge) f93be0103fe9cb92a376848fc534233b48977918 test: make sure keypool sizes do not change on `getrawchangeaddress`/`getnewaddress` failures (UdjinM6) 7c08ccf19bf0a7970f543a3756d8861f81c17197 wallet: Avoid updating `ReserveDestination::nIndex` when `GetReservedDestination` fails (UdjinM6) Pull request description: Backport: * #29510 * #29412 * #29524 ACKs for top commit: glozow: utACK 27cfda1baec63ee4fb0f743576227528104fe495 Tree-SHA512: 37feadd65d9ea55c0a92c9d2a6f74f87cafed3bc67f8deeaaafc5b7042f954e55ea34816612e1a49088f4f1906f104e00c7c3bec7affd1c1f48220b57a8769c5
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp127
1 files changed, 89 insertions, 38 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index 1fd8f0e326..5ef371b97b 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -3486,6 +3486,60 @@ static bool CheckBlockHeader(const CBlockHeader& block, BlockValidationState& st
return true;
}
+static bool CheckMerkleRoot(const CBlock& block, BlockValidationState& state)
+{
+ if (block.m_checked_merkle_root) return true;
+
+ bool mutated;
+ uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
+ if (block.hashMerkleRoot != hashMerkleRoot2)
+ return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txnmrklroot", "hashMerkleRoot mismatch");
+
+ // Check for merkle tree malleability (CVE-2012-2459): repeating sequences
+ // of transactions in a block without affecting the merkle root of a block,
+ // while still invalidating it.
+ if (mutated)
+ return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txns-duplicate", "duplicate transaction");
+
+ block.m_checked_merkle_root = true;
+ return true;
+}
+
+static bool CheckWitnessMalleation(const CBlock& block, bool expect_witness_commitment, BlockValidationState& state)
+{
+ if (expect_witness_commitment) {
+ if (block.m_checked_witness_commitment) return true;
+
+ int commitpos = GetWitnessCommitmentIndex(block);
+ if (commitpos != NO_WITNESS_COMMITMENT) {
+ bool malleated = false;
+ uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated);
+ // The malleation check is ignored; as the transaction tree itself
+ // already does not permit it, it is impossible to trigger in the
+ // witness tree.
+ if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) {
+ return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-witness-nonce-size", strprintf("%s : invalid witness reserved value size", __func__));
+ }
+ CHash256().Write(hashWitness).Write(block.vtx[0]->vin[0].scriptWitness.stack[0]).Finalize(hashWitness);
+ if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
+ return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-witness-merkle-match", strprintf("%s : witness merkle commitment mismatch", __func__));
+ }
+
+ block.m_checked_witness_commitment = true;
+ return true;
+ }
+ }
+
+ // No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room for spam
+ for (const auto& tx : block.vtx) {
+ if (tx->HasWitness()) {
+ return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "unexpected-witness", strprintf("%s : unexpected witness data found", __func__));
+ }
+ }
+
+ return true;
+}
+
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot)
{
// These are checks that are independent of context.
@@ -3504,17 +3558,8 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
}
// Check the merkle root.
- if (fCheckMerkleRoot) {
- bool mutated;
- uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
- if (block.hashMerkleRoot != hashMerkleRoot2)
- return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txnmrklroot", "hashMerkleRoot mismatch");
-
- // Check for merkle tree malleability (CVE-2012-2459): repeating sequences
- // of transactions in a block without affecting the merkle root of a block,
- // while still invalidating it.
- if (mutated)
- return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txns-duplicate", "duplicate transaction");
+ if (fCheckMerkleRoot && !CheckMerkleRoot(block, state)) {
+ return false;
}
// All potential-corruption validation must be done before we do any
@@ -3605,6 +3650,37 @@ bool HasValidProofOfWork(const std::vector<CBlockHeader>& headers, const Consens
[&](const auto& header) { return CheckProofOfWork(header.GetHash(), header.nBits, consensusParams);});
}
+bool IsBlockMutated(const CBlock& block, bool check_witness_root)
+{
+ BlockValidationState state;
+ if (!CheckMerkleRoot(block, state)) {
+ LogPrint(BCLog::VALIDATION, "Block mutated: %s\n", state.ToString());
+ return true;
+ }
+
+ if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
+ // Consider the block mutated if any transaction is 64 bytes in size (see 3.1
+ // in "Weaknesses in Bitcoin’s Merkle Root Construction":
+ // https://lists.linuxfoundation.org/pipermail/bitcoin-dev/attachments/20190225/a27d8837/attachment-0001.pdf).
+ //
+ // Note: This is not a consensus change as this only applies to blocks that
+ // don't have a coinbase transaction and would therefore already be invalid.
+ return std::any_of(block.vtx.begin(), block.vtx.end(),
+ [](auto& tx) { return ::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) == 64; });
+ } else {
+ // Theoretically it is still possible for a block with a 64 byte
+ // coinbase transaction to be mutated but we neglect that possibility
+ // here as it requires at least 224 bits of work.
+ }
+
+ if (!CheckWitnessMalleation(block, check_witness_root, state)) {
+ LogPrint(BCLog::VALIDATION, "Block mutated: %s\n", state.ToString());
+ return true;
+ }
+
+ return false;
+}
+
arith_uint256 CalculateHeadersWork(const std::vector<CBlockHeader>& headers)
{
arith_uint256 total_work{0};
@@ -3713,33 +3789,8 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
// * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are
// {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness reserved value). In case there are
// multiple, the last one is used.
- bool fHaveWitness = false;
- if (DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_SEGWIT)) {
- int commitpos = GetWitnessCommitmentIndex(block);
- if (commitpos != NO_WITNESS_COMMITMENT) {
- bool malleated = false;
- uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated);
- // The malleation check is ignored; as the transaction tree itself
- // already does not permit it, it is impossible to trigger in the
- // witness tree.
- if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) {
- return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-witness-nonce-size", strprintf("%s : invalid witness reserved value size", __func__));
- }
- CHash256().Write(hashWitness).Write(block.vtx[0]->vin[0].scriptWitness.stack[0]).Finalize(hashWitness);
- if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
- return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-witness-merkle-match", strprintf("%s : witness merkle commitment mismatch", __func__));
- }
- fHaveWitness = true;
- }
- }
-
- // No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room for spam
- if (!fHaveWitness) {
- for (const auto& tx : block.vtx) {
- if (tx->HasWitness()) {
- return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "unexpected-witness", strprintf("%s : unexpected witness data found", __func__));
- }
- }
+ if (!CheckWitnessMalleation(block, DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_SEGWIT), state)) {
+ return false;
}
// After the coinbase witness reserved value and commitment are verified,