test: ensure `ValidateInputsStandardness` optionally returns debug string
What changed, and why it matters
This commit only adds new test cases to Bitcoin Core's unit tests. It checks that a function called ValidateInputsStandardness correctly returns specific error messages when transactions have unusual or non-standard inputs. There is no change to the actual network code, consensus rules, or wallet behavior—only more thorough testing of existing behavior.
No security action required. This is a test-only improvement and can be reviewed as normal quality assurance.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change is confined to src/test/script_p2sh_tests.cpp in the ValidateInputsStandardness test. It expands the mock transaction outputs from 7 to 10 and adds test scenarios for P2SH redeem scripts missing, non-standard outputs, malformed P2SH scriptsigs containing OP_RETURN, and undefined witness programs. Each new scenario asserts the expected reject reason and debug message returned by ValidateInputsStandardness. No production code is modified.
Changed components
src/test/script_p2sh_tests.cppInspect captured patch +85 / −4
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index 858723d4..0bb2ba41 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE(ValidateInputsStandardness)
keys.push_back(key[i].GetPubKey());
CMutableTransaction txFrom;
- txFrom.vout.resize(7);
+ txFrom.vout.resize(10);
// First three are standard:
CScript pay1 = GetScriptForDestination(PKHash(key[0].GetPubKey()));
@@ -334,7 +334,24 @@ BOOST_AUTO_TEST_CASE(ValidateInputsStandardness)
CScript twentySigops; twentySigops << OP_CHECKMULTISIG;
BOOST_CHECK(keystore.AddCScript(twentySigops));
txFrom.vout[6].scriptPubKey = GetScriptForDestination(ScriptHash(twentySigops));
- txFrom.vout[6].nValue = 6000;
+ txFrom.vout[6].nValue = 3000;
+
+ // vout[7] is non-standard because it lacks sigops
+ CScript no_sigops;
+ txFrom.vout[7].scriptPubKey = no_sigops;
+ txFrom.vout[7].nValue = 1000;
+
+ // vout [8] is non-standard because it contains OP_RETURN in its redeemScript.
+ static const unsigned char op_return[] = {OP_RETURN};
+ const auto op_return_script = CScript(op_return, op_return + sizeof(op_return));
+ txFrom.vout[8].scriptPubKey = GetScriptForDestination(ScriptHash(op_return_script));
+ txFrom.vout[8].nValue = 1000;
+
+ // vout[9] is non-standard because its witness is unknown
+ CScript witnessUnknown;
+ witnessUnknown << OP_16 << ToByteVector(uint256::ONE);
+ txFrom.vout[9].scriptPubKey = witnessUnknown;
+ txFrom.vout[9].nValue = 1000;
AddCoins(coins, CTransaction(txFrom), 0);
@@ -370,6 +387,7 @@ BOOST_AUTO_TEST_CASE(ValidateInputsStandardness)
BOOST_CHECK(coinbase_tx.IsCoinBase());
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(coinbase_tx, coins), 0U);
+ // TxoutType::SCRIPTHASH
CMutableTransaction txToNonStd1;
txToNonStd1.vout.resize(1);
txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
@@ -379,7 +397,11 @@ BOOST_AUTO_TEST_CASE(ValidateInputsStandardness)
txToNonStd1.vin[0].prevout.hash = txFrom.GetHash();
txToNonStd1.vin[0].scriptSig << std::vector<unsigned char>(sixteenSigops.begin(), sixteenSigops.end());
- BOOST_CHECK(::ValidateInputsStandardness(CTransaction(txToNonStd1), coins).IsInvalid());
+ const auto txToNonStd1_res = ::ValidateInputsStandardness(CTransaction(txToNonStd1), coins);
+ BOOST_CHECK(txToNonStd1_res.IsInvalid());
+ BOOST_CHECK_EQUAL(txToNonStd1_res.GetRejectReason(), "bad-txns-nonstandard-inputs");
+ BOOST_CHECK_EQUAL(txToNonStd1_res.GetDebugMessage(), "p2sh redeemscript sigops exceed limit (input 0: 16 > 15)");
+
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd1), coins), 16U);
CMutableTransaction txToNonStd2;
@@ -391,8 +413,67 @@ BOOST_AUTO_TEST_CASE(ValidateInputsStandardness)
txToNonStd2.vin[0].prevout.hash = txFrom.GetHash();
txToNonStd2.vin[0].scriptSig << std::vector<unsigned char>(twentySigops.begin(), twentySigops.end());
- BOOST_CHECK(::ValidateInputsStandardness(CTransaction(txToNonStd2), coins).IsInvalid());
+ const auto txToNonStd2_res = ::ValidateInputsStandardness(CTransaction(txToNonStd2), coins);
+ BOOST_CHECK(txToNonStd2_res.IsInvalid());
+ BOOST_CHECK_EQUAL(txToNonStd2_res.GetRejectReason(), "bad-txns-nonstandard-inputs");
+ BOOST_CHECK_EQUAL(txToNonStd2_res.GetDebugMessage(), "p2sh redeemscript sigops exceed limit (input 0: 20 > 15)");
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd2), coins), 20U);
+
+ CMutableTransaction txToNonStd2_no_scriptSig;
+ txToNonStd2_no_scriptSig.vout.resize(1);
+ txToNonStd2_no_scriptSig.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
+ txToNonStd2_no_scriptSig.vout[0].nValue = 1000;
+ txToNonStd2_no_scriptSig.vin.resize(1);
+ txToNonStd2_no_scriptSig.vin[0].prevout.n = 6;
+ txToNonStd2_no_scriptSig.vin[0].prevout.hash = txFrom.GetHash();
+
+ const auto txToNonStd2_no_scriptSig_res = ::ValidateInputsStandardness(CTransaction(txToNonStd2_no_scriptSig), coins);
+ BOOST_CHECK(txToNonStd2_no_scriptSig_res.IsInvalid());
+ BOOST_CHECK_EQUAL(txToNonStd2_no_scriptSig_res.GetRejectReason(), "bad-txns-nonstandard-inputs");
+ BOOST_CHECK_EQUAL(txToNonStd2_no_scriptSig_res.GetDebugMessage(), "input 0 P2SH redeemscript missing");
+ BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd2), coins), 20U);
+
+ // TxoutType::NONSTANDARD
+ CMutableTransaction txToNonStd3;
+ txToNonStd3.vout.resize(1);
+ txToNonStd3.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
+ txToNonStd3.vout[0].nValue = 1000;
+ txToNonStd3.vin.resize(1);
+ txToNonStd3.vin[0].prevout.n = 7;
+ txToNonStd3.vin[0].prevout.hash = txFrom.GetHash();
+
+ const auto txToNonStd3_res = ::ValidateInputsStandardness(CTransaction(txToNonStd3), coins);
+ BOOST_CHECK(txToNonStd3_res.IsInvalid());
+ BOOST_CHECK_EQUAL(txToNonStd3_res.GetRejectReason(), "bad-txns-nonstandard-inputs");
+ BOOST_CHECK_EQUAL(txToNonStd3_res.GetDebugMessage(), "input 0 script unknown");
+
+ // TxoutType::INCORRECT_SCRIPTSIG
+ CMutableTransaction txToNonStd4;
+ txToNonStd4.vout.resize(1);
+ txToNonStd4.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
+ txToNonStd4.vout[0].nValue = 1000;
+ txToNonStd4.vin.resize(1);
+ txToNonStd4.vin[0].prevout.n = 8;
+ txToNonStd4.vin[0].prevout.hash = txFrom.GetHash();
+ txToNonStd4.vin[0].scriptSig = op_return_script;
+
+ const auto txToNonStd4_res = ::ValidateInputsStandardness(CTransaction(txToNonStd4), coins);
+ BOOST_CHECK(txToNonStd4_res.IsInvalid());
+ BOOST_CHECK_EQUAL(txToNonStd4_res.GetRejectReason(), "bad-txns-nonstandard-inputs");
+ BOOST_CHECK_EQUAL(txToNonStd4_res.GetDebugMessage(), "p2sh scriptsig malformed (input 0: OP_RETURN was encountered)");
+
+ // TxoutType::WITNESS_UNKNOWN
+ CMutableTransaction txWitnessUnknown;
+ txWitnessUnknown.vout.resize(1);
+ txWitnessUnknown.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
+ txWitnessUnknown.vout[0].nValue = 1000;
+ txWitnessUnknown.vin.resize(1);
+ txWitnessUnknown.vin[0].prevout.n = 9;
+ txWitnessUnknown.vin[0].prevout.hash = txFrom.GetHash();
+ const auto txWitnessUnknown_res = ::ValidateInputsStandardness(CTransaction(txWitnessUnknown), coins);
+ BOOST_CHECK(txWitnessUnknown_res.IsInvalid());
+ BOOST_CHECK_EQUAL(txWitnessUnknown_res.GetRejectReason(), "bad-txns-nonstandard-inputs");
+ BOOST_CHECK_EQUAL(txWitnessUnknown_res.GetDebugMessage(), "input 0 witness program is undefined");
}
BOOST_AUTO_TEST_SUITE_END()
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.