diff options
author | glozow <gloriajzhao@gmail.com> | 2024-03-11 09:46:22 +0000 |
---|---|---|
committer | glozow <gloriajzhao@gmail.com> | 2024-03-11 09:54:18 +0000 |
commit | c33e83a53a863e55413bee2893716b93165bf656 (patch) | |
tree | efacfa32b84fbb4f2ebd0594180cae34094d1d2f /src/validation.cpp | |
parent | a718bfafe7ad38bab8a5782ae0db719480984238 (diff) | |
parent | c68d4d0ac5b8537ba5e1c0c512b807768e1c5c1f (diff) |
Merge bitcoin/bitcoin#29509: [26.x] backports and final changes for v26.1rc2v26.1rc2
c68d4d0ac5b8537ba5e1c0c512b807768e1c5c1f [doc] update manual pages for v26.1rc2 (glozow)
bd715bfb3030f392b3b19f9a05aada48e385a0d9 [build] bump version to v26.1rc2 (glozow)
b6d006d2a2b840e4a5af96c8d838e1cf589d3bce update release notes 26.1 (glozow)
fce992b38e59c90babe505eda0d72f05d79eb2f3 fuzz: restrict fopencookie usage to Linux & FreeBSD (fanquake)
40c56a4d1341017b02dcb71882b1b1f03f880b1d test: make sure keypool sizes do not change on `getrawchangeaddress`/`getnewaddress` failures (UdjinM6)
7c82b2758c6bcfb8a94d2086f0d40088286815e8 wallet: Avoid updating `ReserveDestination::nIndex` when `GetReservedDestination` fails (UdjinM6)
b5419ce6b621121bb1a0ec497968eb16cc012c39 p2p: Don't consider blocks mutated if they don't connect to known prev block (Greg Sanders)
0535c253fe71ae9d875827cafed41a8889f4a702 [test] IsBlockMutated unit tests (dergoegge)
8141498f3ad3ae9c42c32346ee73dc7f29e72cb5 [validation] Cache merkle root and witness commitment checks (dergoegge)
0c5c5962cbfdfd532cebc6706d5b838488b89d53 [test] Add regression test for #27608 (dergoegge)
24736350e932799c66c999470fa3837e25576fc7 [net processing] Don't process mutated blocks (dergoegge)
50c0b61a9d562240d5fe4bd79324b0c0e79caa5c [validation] Merkle root malleation should be caught by IsBlockMutated (dergoegge)
aff368fa817b065d99729186d304fff02f6e527b [validation] Introduce IsBlockMutated (dergoegge)
076c67c3aae424e58863dde3bc37cedecc496935 [refactor] Cleanup merkle root checks (dergoegge)
97a1d0a45959a29464ae73087c7a0adcdebd5a61 [validation] Isolate merkle root checks (dergoegge)
4ac0eb543d028379bb2b86ab08bbbb2f9f48d5b1 test: Drop `x` modifier in `fsbridge::fopen` call for mingw builds (Hennadii Stepanov)
Pull request description:
Includes:
- #29357
- #29412
- #29524
- #29510
- #29529
Also does:
- update to release notes
- bump to rc2
- manpages
- (no changes to bitcoin.conf)
ACKs for top commit:
achow101:
ACK c68d4d0ac5b8537ba5e1c0c512b807768e1c5c1f
Tree-SHA512: 2f8c3dd705e3f9f33403b3cc17e8006510ff827d7dbd609b09732a1669964e9b001cfecdc63d8d8daeb8f39c652e1e4ad0aac873d44d259c21803de85688ed2b
Diffstat (limited to 'src/validation.cpp')
-rw-r--r-- | src/validation.cpp | 154 |
1 files changed, 116 insertions, 38 deletions
diff --git a/src/validation.cpp b/src/validation.cpp index 5d435fef8b..f02d1e32a4 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3621,6 +3621,87 @@ 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 merkle_root = BlockMerkleRoot(block, &mutated); + if (block.hashMerkleRoot != merkle_root) { + return state.Invalid( + /*result=*/BlockValidationResult::BLOCK_MUTATED, + /*reject_reason=*/"bad-txnmrklroot", + /*debug_message=*/"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( + /*result=*/BlockValidationResult::BLOCK_MUTATED, + /*reject_reason=*/"bad-txns-duplicate", + /*debug_message=*/"duplicate transaction"); + } + + block.m_checked_merkle_root = true; + return true; +} + +/** CheckWitnessMalleation performs checks for block malleation with regard to + * its witnesses. + * + * Note: If the witness commitment is expected (i.e. `expect_witness_commitment + * = true`), then the block is required to have at least one transaction and the + * first transaction needs to have at least one input. */ +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) { + assert(!block.vtx.empty() && !block.vtx[0]->vin.empty()); + const auto& witness_stack{block.vtx[0]->vin[0].scriptWitness.stack}; + + if (witness_stack.size() != 1 || witness_stack[0].size() != 32) { + return state.Invalid( + /*result=*/BlockValidationResult::BLOCK_MUTATED, + /*reject_reason=*/"bad-witness-nonce-size", + /*debug_message=*/strprintf("%s : invalid witness reserved value size", __func__)); + } + + // The malleation check is ignored; as the transaction tree itself + // already does not permit it, it is impossible to trigger in the + // witness tree. + uint256 hash_witness = BlockWitnessMerkleRoot(block, /*mutated=*/nullptr); + + CHash256().Write(hash_witness).Write(witness_stack[0]).Finalize(hash_witness); + if (memcmp(hash_witness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) { + return state.Invalid( + /*result=*/BlockValidationResult::BLOCK_MUTATED, + /*reject_reason=*/"bad-witness-merkle-match", + /*debug_message=*/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( + /*result=*/BlockValidationResult::BLOCK_MUTATED, + /*reject_reason=*/"unexpected-witness", + /*debug_message=*/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. @@ -3639,17 +3720,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 @@ -3740,6 +3812,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}; @@ -3848,33 +3951,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, |