Skip to content

Commit 90ed98a

Browse files
committed
Merge #17080: consensus: Explain why fCheckDuplicateInputs can not be skipped and remove it
fa92813 consensus: Explain why fCheckDuplicateInputs can not be skipped and remove it (MarcoFalke) Pull request description: As a follow up to CVE-2018-17144, this removes the unused `fCheckDuplicateInputs` parameter and explains why the test can not be disabled. Apart from protecting against a dumb accident in the future, this should document the logic in the code. There is a technical write-up that explains how the underlying coins database behaves if this test is skipped: https://bitcoincore.org/en/2018/09/20/notice/#technical-details. However, it does not explicitly mention why the test can not be skipped. I hope my code comment does that. ACKs for top commit: jnewbery: ACK fa92813 amitiuttarwar: utACK fa92813 Empact: Code review ACK bitcoin/bitcoin@fa92813 promag: ACK fa92813. Tree-SHA512: fc1ef670f1a467c543b84f704b9bd8cc7a59a9f707be048bd9b4e85fe70830702aa560a880efa2c840bb43818ab44dfdc611104df04db2ddc14ff92f46bfb28e
2 parents 366753e + fa92813 commit 90ed98a

File tree

4 files changed

+13
-18
lines changed

4 files changed

+13
-18
lines changed

src/consensus/tx_check.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <primitives/transaction.h>
88
#include <consensus/validation.h>
99

10-
bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
10+
bool CheckTransaction(const CTransaction& tx, CValidationState& state)
1111
{
1212
// Basic checks that don't depend on any context
1313
if (tx.vin.empty())
@@ -31,14 +31,15 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
3131
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-txouttotal-toolarge");
3232
}
3333

34-
// Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
35-
if (fCheckDuplicateInputs) {
36-
std::set<COutPoint> vInOutPoints;
37-
for (const auto& txin : tx.vin)
38-
{
39-
if (!vInOutPoints.insert(txin.prevout).second)
40-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-inputs-duplicate");
41-
}
34+
// Check for duplicate inputs (see CVE-2018-17144)
35+
// While Consensus::CheckTxInputs does check if all inputs of a tx are available, and UpdateCoins marks all inputs
36+
// of a tx as spent, it does not check if the tx has duplicate inputs.
37+
// Failure to run this check will result in either a crash or an inflation bug, depending on the implementation of
38+
// the underlying coins database.
39+
std::set<COutPoint> vInOutPoints;
40+
for (const auto& txin : tx.vin) {
41+
if (!vInOutPoints.insert(txin.prevout).second)
42+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-inputs-duplicate");
4243
}
4344

4445
if (tx.IsCoinBase())

src/consensus/tx_check.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
class CTransaction;
1616
class CValidationState;
1717

18-
bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true);
18+
bool CheckTransaction(const CTransaction& tx, CValidationState& state);
1919

2020
#endif // BITCOIN_CONSENSUS_TX_CHECK_H

src/test/fuzz/transaction.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
4343
}
4444

4545
CValidationState state_with_dupe_check;
46-
const bool valid_with_dupe_check = CheckTransaction(tx, state_with_dupe_check, /* fCheckDuplicateInputs= */ true);
47-
CValidationState state_without_dupe_check;
48-
const bool valid_without_dupe_check = CheckTransaction(tx, state_without_dupe_check, /* fCheckDuplicateInputs= */ false);
49-
if (valid_with_dupe_check) {
50-
assert(valid_without_dupe_check);
51-
}
46+
(void)CheckTransaction(tx, state_with_dupe_check);
5247

5348
const CFeeRate dust_relay_fee{DUST_RELAY_TX_FEE};
5449
std::string reason;

src/validation.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3301,9 +3301,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
33013301
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cb-multiple", "more than one coinbase");
33023302

33033303
// Check transactions
3304-
// Must check for duplicate inputs (see CVE-2018-17144)
33053304
for (const auto& tx : block.vtx)
3306-
if (!CheckTransaction(*tx, state, true))
3305+
if (!CheckTransaction(*tx, state))
33073306
return state.Invalid(state.GetReason(), false, state.GetRejectReason(),
33083307
strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage()));
33093308

0 commit comments

Comments
 (0)