Skip to content

Commit 6d85435

Browse files
committed
Merge #19620: Add txids with non-standard inputs to reject filter
9f88ded test addition of unknown segwit spends to txid reject filter (Gregory Sanders) 7989901 Add txids with non-standard inputs to reject filter (Suhas Daftuar) Pull request description: 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. ACKs for top commit: ajtowns: ACK 9f88ded - code review jnewbery: Code review ACK 9f88ded ariard: Code Review/Tested ACK 9f88ded naumenkogs: utACK 9f88ded jonatack: ACK 9f88ded Tree-SHA512: 1e93c0a5b68cb432524780ffc0093db893911fdfed9e2ed17f888e59114cc75d2a07062aefad4e5ce2e87c9270886117a8abb3c78fb889c9b9f31967f1777148
2 parents 82127d2 + 9f88ded commit 6d85435

File tree

5 files changed

+55
-8
lines changed

5 files changed

+55
-8
lines changed

src/consensus/validation.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ enum class TxValidationResult {
2626
* is uninteresting.
2727
*/
2828
TX_RECENT_CONSENSUS_CHANGE,
29-
TX_NOT_STANDARD, //!< didn't meet our local policy rules
29+
TX_INPUTS_NOT_STANDARD, //!< inputs (covered by txid) failed policy rules
30+
TX_NOT_STANDARD, //!< otherwise didn't meet our local policy rules
3031
TX_MISSING_INPUTS, //!< transaction was missing some of its inputs
3132
TX_PREMATURE_SPEND, //!< transaction spends a coinbase too early, or violates locktime/sequence locks
3233
/**

src/net_processing.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ namespace {
187187
* million to make it highly unlikely for users to have issues with this
188188
* filter.
189189
*
190-
* We only need to add wtxids to this filter. For non-segwit
190+
* We typically only add wtxids to this filter. For non-segwit
191191
* transactions, the txid == wtxid, so this only prevents us from
192192
* re-downloading non-segwit transactions when communicating with
193193
* non-wtxidrelay peers -- which is important for avoiding malleation
@@ -196,6 +196,12 @@ namespace {
196196
* the reject filter store wtxids is exactly what we want to avoid
197197
* redownload of a rejected transaction.
198198
*
199+
* In cases where we can tell that a segwit transaction will fail
200+
* validation no matter the witness, we may add the txid of such
201+
* transaction to the filter as well. This can be helpful when
202+
* communicating with txid-relay peers or if we were to otherwise fetch a
203+
* transaction via txid (eg in our orphan handling).
204+
*
199205
* Memory used: 1.3 MB
200206
*/
201207
std::unique_ptr<CRollingBloomFilter> recentRejects GUARDED_BY(cs_main);
@@ -1161,6 +1167,7 @@ static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state,
11611167
}
11621168
// Conflicting (but not necessarily invalid) data or different policy:
11631169
case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE:
1170+
case TxValidationResult::TX_INPUTS_NOT_STANDARD:
11641171
case TxValidationResult::TX_NOT_STANDARD:
11651172
case TxValidationResult::TX_MISSING_INPUTS:
11661173
case TxValidationResult::TX_PREMATURE_SPEND:
@@ -2052,6 +2059,19 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
20522059
// if we start doing this too early.
20532060
assert(recentRejects);
20542061
recentRejects->insert(orphanTx.GetWitnessHash());
2062+
// If the transaction failed for TX_INPUTS_NOT_STANDARD,
2063+
// then we know that the witness was irrelevant to the policy
2064+
// failure, since this check depends only on the txid
2065+
// (the scriptPubKey being spent is covered by the txid).
2066+
// Add the txid to the reject filter to prevent repeated
2067+
// processing of this transaction in the event that child
2068+
// transactions are later received (resulting in
2069+
// parent-fetching by txid via the orphan-handling logic).
2070+
if (orphan_state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && orphanTx.GetWitnessHash() != orphanTx.GetHash()) {
2071+
// We only add the txid if it differs from the wtxid, to
2072+
// avoid wasting entries in the rolling bloom filter.
2073+
recentRejects->insert(orphanTx.GetHash());
2074+
}
20552075
}
20562076
EraseOrphanTx(orphanHash);
20572077
done = true;
@@ -2943,7 +2963,7 @@ void ProcessMessage(
29432963

29442964
// We do the AlreadyHave() check using wtxid, rather than txid - in the
29452965
// absence of witness malleation, this is strictly better, because the
2946-
// recent rejects filter may contain the wtxid but will never contain
2966+
// recent rejects filter may contain the wtxid but rarely contains
29472967
// the txid of a segwit transaction that has been rejected.
29482968
// In the presence of witness malleation, it's possible that by only
29492969
// doing the check with wtxid, we could overlook a transaction which
@@ -3035,6 +3055,17 @@ void ProcessMessage(
30353055
// if we start doing this too early.
30363056
assert(recentRejects);
30373057
recentRejects->insert(tx.GetWitnessHash());
3058+
// If the transaction failed for TX_INPUTS_NOT_STANDARD,
3059+
// then we know that the witness was irrelevant to the policy
3060+
// failure, since this check depends only on the txid
3061+
// (the scriptPubKey being spent is covered by the txid).
3062+
// Add the txid to the reject filter to prevent repeated
3063+
// processing of this transaction in the event that child
3064+
// transactions are later received (resulting in
3065+
// parent-fetching by txid via the orphan-handling logic).
3066+
if (state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && tx.GetWitnessHash() != tx.GetHash()) {
3067+
recentRejects->insert(tx.GetHash());
3068+
}
30383069
if (RecursiveDynamicUsage(*ptx) < 100000) {
30393070
AddToCompactExtraTransactions(ptx);
30403071
}

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
TxoutType whichType = Solver(prev.scriptPubKey, vSolutions);
167-
if (whichType == TxoutType::NONSTANDARD) {
169+
if (whichType == TxoutType::NONSTANDARD || whichType == TxoutType::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 == TxoutType::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
@@ -688,8 +688,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
688688
}
689689

690690
// Check for non-standard pay-to-script-hash in inputs
691-
if (fRequireStandard && !AreInputsStandard(tx, m_view))
692-
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-nonstandard-inputs");
691+
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
692+
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
693+
}
693694

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

test/functional/p2p_segwit.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,16 +1418,24 @@ def test_segwit_versions(self):
14181418
temp_utxo.pop() # last entry in temp_utxo was the output we just spent
14191419
temp_utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
14201420

1421-
# Spend everything in temp_utxo back to an OP_TRUE output.
1421+
# Spend everything in temp_utxo into an segwit v1 output.
14221422
tx3 = CTransaction()
14231423
total_value = 0
14241424
for i in temp_utxo:
14251425
tx3.vin.append(CTxIn(COutPoint(i.sha256, i.n), b""))
14261426
tx3.wit.vtxinwit.append(CTxInWitness())
14271427
total_value += i.nValue
14281428
tx3.wit.vtxinwit[-1].scriptWitness.stack = [witness_program]
1429-
tx3.vout.append(CTxOut(total_value - 1000, CScript([OP_TRUE])))
1429+
tx3.vout.append(CTxOut(total_value - 1000, script_pubkey))
14301430
tx3.rehash()
1431+
1432+
# First we test this transaction against fRequireStandard=true node
1433+
# making sure the txid is added to the reject filter
1434+
self.std_node.announce_tx_and_wait_for_getdata(tx3)
1435+
test_transaction_acceptance(self.nodes[1], self.std_node, tx3, with_witness=True, accepted=False, reason="bad-txns-nonstandard-inputs")
1436+
# Now the node will no longer ask for getdata of this transaction when advertised by same txid
1437+
self.std_node.announce_tx_and_wait_for_getdata(tx3, timeout=5, success=False)
1438+
14311439
# Spending a higher version witness output is not allowed by policy,
14321440
# even with fRequireStandard=false.
14331441
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=False, reason="reserved for soft-fork upgrades")

0 commit comments

Comments
 (0)