aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Schnelli <dev@jonasschnelli.ch>2020-08-28 09:08:31 +0200
committerJonas Schnelli <dev@jonasschnelli.ch>2020-08-28 09:08:44 +0200
commitaee9d2306ad1e1eed24dc57d181c75bd2344291d (patch)
tree82f0d0c06a774e3f1b2ca3687b13fa58c93aa481
parent89a6bb924571dfdda5376fa4de70921b1ccecdf3 (diff)
parent52c3bec1bad4fc602a3911b44de48c6b0dbdfd25 (diff)
downloadbitcoin-aee9d2306ad1e1eed24dc57d181c75bd2344291d.tar.xz
Merge #19681: 0.19: Add txids with non-standard inputs to reject filter
52c3bec1bad4fc602a3911b44de48c6b0dbdfd25 test addition of unknown segwit spends to txid reject filter (Gregory Sanders) 2ea826cfc46ee8edfca059d0fd95ebe03122f9f2 Add txids with non-standard inputs to reject filter (Suhas Daftuar) Pull request description: Backport of #19620 to 0.19. ACKs for top commit: instagibbs: utACK https://github.com/bitcoin/bitcoin/pull/19681/commits/52c3bec1bad4fc602a3911b44de48c6b0dbdfd25 jnewbery: utACK 52c3bec1ba jonasschnelli: utACK 52c3bec1bad4fc602a3911b44de48c6b0dbdfd25 Tree-SHA512: 76b52d3fb0f9d88674dd186dee611bf0a2349b17549ef7909b4b37ace5b64d4edce56d71410e7b743e7e7d18855b84ff4b555a5edac26f67786abb9a264fa256
-rw-r--r--src/consensus/validation.h4
-rw-r--r--src/net_processing.cpp15
-rw-r--r--src/policy/policy.cpp8
-rw-r--r--src/validation.cpp5
-rwxr-xr-xtest/functional/p2p_segwit.py12
5 files changed, 36 insertions, 8 deletions
diff --git a/src/consensus/validation.h b/src/consensus/validation.h
index 2e23f4b3a4..dbef958e19 100644
--- a/src/consensus/validation.h
+++ b/src/consensus/validation.h
@@ -48,7 +48,8 @@ enum class ValidationInvalidReason {
BLOCK_TIME_FUTURE, //!< block timestamp was > 2 hours in the future (or our clock is bad)
BLOCK_CHECKPOINT, //!< the block failed to meet one of our checkpoints
// Only loose txn:
- TX_NOT_STANDARD, //!< didn't meet our local policy rules
+ TX_INPUTS_NOT_STANDARD, //!< inputs (covered by txid) failed policy rules
+ TX_NOT_STANDARD, //!< otherwise didn't meet our local policy rules
TX_MISSING_INPUTS, //!< a transaction was missing some of its inputs
TX_PREMATURE_SPEND, //!< transaction spends a coinbase too early, or violates locktime/sequence locks
/**
@@ -72,6 +73,7 @@ inline bool IsTransactionReason(ValidationInvalidReason r)
return r == ValidationInvalidReason::NONE ||
r == ValidationInvalidReason::CONSENSUS ||
r == ValidationInvalidReason::RECENT_CONSENSUS_CHANGE ||
+ r == ValidationInvalidReason::TX_INPUTS_NOT_STANDARD ||
r == ValidationInvalidReason::TX_NOT_STANDARD ||
r == ValidationInvalidReason::TX_PREMATURE_SPEND ||
r == ValidationInvalidReason::TX_MISSING_INPUTS ||
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 3d0efa041d..4a183f0c9a 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1055,6 +1055,7 @@ static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool v
return true;
case ValidationInvalidReason::RECENT_CONSENSUS_CHANGE:
case ValidationInvalidReason::BLOCK_TIME_FUTURE:
+ case ValidationInvalidReason::TX_INPUTS_NOT_STANDARD:
case ValidationInvalidReason::TX_NOT_STANDARD:
case ValidationInvalidReason::TX_MISSING_INPUTS:
case ValidationInvalidReason::TX_PREMATURE_SPEND:
@@ -1846,10 +1847,15 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
// Probably non-standard or insufficient fee
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
assert(IsTransactionReason(orphan_state.GetReason()));
- if (!orphanTx.HasWitness() && orphan_state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) {
+ if ((!orphanTx.HasWitness() && orphan_state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) ||
+ orphan_state.GetReason() == ValidationInvalidReason::TX_INPUTS_NOT_STANDARD) {
// Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ // However, if the transaction failed for TX_INPUTS_NOT_STANDARD,
+ // then we know that the witness was irrelevant to the policy
+ // failure, since this check depends only on the txid
+ // (the scriptPubKey being spent is covered by the txid).
assert(recentRejects);
recentRejects->insert(orphanHash);
}
@@ -2574,10 +2580,15 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
} else {
assert(IsTransactionReason(state.GetReason()));
- if (!tx.HasWitness() && state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) {
+ if ((!tx.HasWitness() && state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) ||
+ state.GetReason() == ValidationInvalidReason::TX_INPUTS_NOT_STANDARD) {
// Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ // However, if the transaction failed for TX_INPUTS_NOT_STANDARD,
+ // then we know that the witness was irrelevant to the policy
+ // failure, since this check depends only on the txid
+ // (the scriptPubKey being spent is covered by the txid).
assert(recentRejects);
recentRejects->insert(tx.GetHash());
if (RecursiveDynamicUsage(*ptx) < 100000) {
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 51de5841ec..636a916602 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -152,6 +152,8 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR
* script can be anything; an attacker could use a very
* expensive-to-check-upon-redemption script like:
* DUP CHECKSIG DROP ... repeated 100 times... OP_1
+ *
+ * Note that only the non-witness portion of the transaction is checked here.
*/
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
{
@@ -164,7 +166,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
std::vector<std::vector<unsigned char> > vSolutions;
txnouttype whichType = Solver(prev.scriptPubKey, vSolutions);
- if (whichType == TX_NONSTANDARD) {
+ if (whichType == TX_NONSTANDARD || whichType == TX_WITNESS_UNKNOWN) {
+ // WITNESS_UNKNOWN failures are typically also caught with a policy
+ // flag in the script interpreter, but it can be helpful to catch
+ // this type of NONSTANDARD transaction earlier in transaction
+ // validation.
return false;
} else if (whichType == TX_SCRIPTHASH) {
std::vector<std::vector<unsigned char> > stack;
diff --git a/src/validation.cpp b/src/validation.cpp
index ac98bd61c7..bdf3c128cc 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -678,8 +678,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
// Check for non-standard pay-to-script-hash in inputs
- if (fRequireStandard && !AreInputsStandard(tx, m_view))
- return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
+ if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
+ return state.Invalid(ValidationInvalidReason::TX_INPUTS_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
+ }
// Check for non-standard witness in P2WSH
if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, m_view))
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 98f6b1d71d..760cedde3a 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -1391,7 +1391,7 @@ class SegWitTest(BitcoinTestFramework):
temp_utxo.pop() # last entry in temp_utxo was the output we just spent
temp_utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
- # Spend everything in temp_utxo back to an OP_TRUE output.
+ # Spend everything in temp_utxo into an segwit v1 output.
tx3 = CTransaction()
total_value = 0
for i in temp_utxo:
@@ -1399,8 +1399,16 @@ class SegWitTest(BitcoinTestFramework):
tx3.wit.vtxinwit.append(CTxInWitness())
total_value += i.nValue
tx3.wit.vtxinwit[-1].scriptWitness.stack = [witness_program]
- tx3.vout.append(CTxOut(total_value - 1000, CScript([OP_TRUE])))
+ tx3.vout.append(CTxOut(total_value - 1000, script_pubkey))
tx3.rehash()
+
+ # First we test this transaction against fRequireStandard=true node
+ # making sure the txid is added to the reject filter
+ self.std_node.announce_tx_and_wait_for_getdata(tx3)
+ test_transaction_acceptance(self.nodes[1], self.std_node, tx3, with_witness=True, accepted=False, reason="bad-txns-nonstandard-inputs")
+ # Now the node will no longer ask for getdata of this transaction when advertised by same txid
+ self.std_node.announce_tx_and_wait_for_getdata(tx3, timeout=5, success=False)
+
# Spending a higher version witness output is not allowed by policy,
# even with fRequireStandard=false.
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=False, reason="reserved for soft-fork upgrades")