Skip to content

Commit 2ea826c

Browse files
committed
Add txids with non-standard inputs to reject filter
Our policy checks for non-standard inputs depend only on the non-witness portion of a transaction: we look up the scriptPubKey of the input being spent from our UTXO set (which is covered by the input txid), and the p2sh checks only rely on the scriptSig portion of the input. Consequently it's safe to add txids of transactions that fail these checks to the reject filter, as the witness is irrelevant to the failure. This is helpful for any situation where we might request the transaction again via txid (either from txid-relay peers, or if we might fetch the transaction via txid due to parent-fetching of orphans). Further, in preparation for future witness versions being deployed on the network, ensure that WITNESS_UNKNOWN transactions are rejected in AreInputsStandard(), so that transactions spending v1 (or greater) witness outputs will fall into this category of having their txid added to the reject filter. Github-Pull: #19620 Rebased-From: 7989901
1 parent 28a9df7 commit 2ea826c

File tree

4 files changed

+26
-6
lines changed

4 files changed

+26
-6
lines changed

src/consensus/validation.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ enum class ValidationInvalidReason {
4848
BLOCK_TIME_FUTURE, //!< block timestamp was > 2 hours in the future (or our clock is bad)
4949
BLOCK_CHECKPOINT, //!< the block failed to meet one of our checkpoints
5050
// Only loose txn:
51-
TX_NOT_STANDARD, //!< didn't meet our local policy rules
51+
TX_INPUTS_NOT_STANDARD, //!< inputs (covered by txid) failed policy rules
52+
TX_NOT_STANDARD, //!< otherwise didn't meet our local policy rules
5253
TX_MISSING_INPUTS, //!< a transaction was missing some of its inputs
5354
TX_PREMATURE_SPEND, //!< transaction spends a coinbase too early, or violates locktime/sequence locks
5455
/**
@@ -72,6 +73,7 @@ inline bool IsTransactionReason(ValidationInvalidReason r)
7273
return r == ValidationInvalidReason::NONE ||
7374
r == ValidationInvalidReason::CONSENSUS ||
7475
r == ValidationInvalidReason::RECENT_CONSENSUS_CHANGE ||
76+
r == ValidationInvalidReason::TX_INPUTS_NOT_STANDARD ||
7577
r == ValidationInvalidReason::TX_NOT_STANDARD ||
7678
r == ValidationInvalidReason::TX_PREMATURE_SPEND ||
7779
r == ValidationInvalidReason::TX_MISSING_INPUTS ||

src/net_processing.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,7 @@ static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool v
10551055
return true;
10561056
case ValidationInvalidReason::RECENT_CONSENSUS_CHANGE:
10571057
case ValidationInvalidReason::BLOCK_TIME_FUTURE:
1058+
case ValidationInvalidReason::TX_INPUTS_NOT_STANDARD:
10581059
case ValidationInvalidReason::TX_NOT_STANDARD:
10591060
case ValidationInvalidReason::TX_MISSING_INPUTS:
10601061
case ValidationInvalidReason::TX_PREMATURE_SPEND:
@@ -1846,10 +1847,15 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
18461847
// Probably non-standard or insufficient fee
18471848
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
18481849
assert(IsTransactionReason(orphan_state.GetReason()));
1849-
if (!orphanTx.HasWitness() && orphan_state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) {
1850+
if ((!orphanTx.HasWitness() && orphan_state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) ||
1851+
orphan_state.GetReason() == ValidationInvalidReason::TX_INPUTS_NOT_STANDARD) {
18501852
// Do not use rejection cache for witness transactions or
18511853
// witness-stripped transactions, as they can have been malleated.
18521854
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
1855+
// However, if the transaction failed for TX_INPUTS_NOT_STANDARD,
1856+
// then we know that the witness was irrelevant to the policy
1857+
// failure, since this check depends only on the txid
1858+
// (the scriptPubKey being spent is covered by the txid).
18531859
assert(recentRejects);
18541860
recentRejects->insert(orphanHash);
18551861
}
@@ -2574,10 +2580,15 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
25742580
}
25752581
} else {
25762582
assert(IsTransactionReason(state.GetReason()));
2577-
if (!tx.HasWitness() && state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) {
2583+
if ((!tx.HasWitness() && state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) ||
2584+
state.GetReason() == ValidationInvalidReason::TX_INPUTS_NOT_STANDARD) {
25782585
// Do not use rejection cache for witness transactions or
25792586
// witness-stripped transactions, as they can have been malleated.
25802587
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
2588+
// However, if the transaction failed for TX_INPUTS_NOT_STANDARD,
2589+
// then we know that the witness was irrelevant to the policy
2590+
// failure, since this check depends only on the txid
2591+
// (the scriptPubKey being spent is covered by the txid).
25812592
assert(recentRejects);
25822593
recentRejects->insert(tx.GetHash());
25832594
if (RecursiveDynamicUsage(*ptx) < 100000) {

src/policy/policy.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR
152152
* script can be anything; an attacker could use a very
153153
* expensive-to-check-upon-redemption script like:
154154
* DUP CHECKSIG DROP ... repeated 100 times... OP_1
155+
*
156+
* Note that only the non-witness portion of the transaction is checked here.
155157
*/
156158
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
157159
{
@@ -164,7 +166,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
164166

165167
std::vector<std::vector<unsigned char> > vSolutions;
166168
txnouttype whichType = Solver(prev.scriptPubKey, vSolutions);
167-
if (whichType == TX_NONSTANDARD) {
169+
if (whichType == TX_NONSTANDARD || whichType == TX_WITNESS_UNKNOWN) {
170+
// WITNESS_UNKNOWN failures are typically also caught with a policy
171+
// flag in the script interpreter, but it can be helpful to catch
172+
// this type of NONSTANDARD transaction earlier in transaction
173+
// validation.
168174
return false;
169175
} else if (whichType == TX_SCRIPTHASH) {
170176
std::vector<std::vector<unsigned char> > stack;

src/validation.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,8 +678,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
678678
}
679679

680680
// Check for non-standard pay-to-script-hash in inputs
681-
if (fRequireStandard && !AreInputsStandard(tx, m_view))
682-
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
681+
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
682+
return state.Invalid(ValidationInvalidReason::TX_INPUTS_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
683+
}
683684

684685
// Check for non-standard witness in P2WSH
685686
if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, m_view))

0 commit comments

Comments
 (0)