From b29ae9efdfeeff774e32ee433ce67d8ed8ecd49f Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Wed, 23 Jul 2025 10:51:06 +1000 Subject: [PATCH] validation: only check input scripts once Previously, we would check failing input scripts twice when considering a transaction for the mempool, in order to distinguish policy failures from consensus failures. This allowed us both to provide a different error message and to discourage peers for consensus failures. Because we are no longer discouraging peers for consensus failures during tx relay, and because checking a script can be expensive, only do this once. Also renames non-mandatory-script-verify-flag error to mempool-script-verify-flag-failed. --- src/validation.cpp | 35 +++++++--------------------- test/functional/data/invalid_txs.py | 7 +++--- test/functional/feature_block.py | 5 +++- test/functional/feature_cltv.py | 18 +++++++------- test/functional/feature_dersig.py | 4 ++-- test/functional/feature_nulldummy.py | 12 +++++----- test/functional/feature_segwit.py | 24 +++++++++---------- test/functional/mempool_accept.py | 2 +- test/functional/p2p_segwit.py | 14 +++++------ test/functional/rpc_packages.py | 4 ++-- 10 files changed, 57 insertions(+), 68 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index cf5f546912d..b013d3b08ce 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2191,34 +2191,17 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state, if (pvChecks) { pvChecks->emplace_back(std::move(check)); } else if (auto result = check(); result.has_value()) { + // Tx failures never trigger disconnections/bans. + // This is so that network splits aren't triggered + // either due to non-consensus relay policies (such as + // non-standard DER encodings or non-null dummy + // arguments) or due to new consensus rules introduced in + // soft forks. if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { - // Check whether the failure was caused by a - // non-mandatory script verification check, such as - // non-standard DER encodings or non-null dummy - // arguments; if so, ensure we return NOT_STANDARD - // instead of CONSENSUS to avoid downstream users - // splitting the network between upgraded and - // non-upgraded nodes by banning CONSENSUS-failing - // data providers. - CScriptCheck check2(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata); - auto mandatory_result = check2(); - if (!mandatory_result.has_value()) { - return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(result->first)), result->second); - } else { - // If the second check failed, it failed due to a mandatory script verification - // flag, but the first check might have failed on a non-mandatory script - // verification flag. - // - // Avoid reporting a mandatory script check failure with a non-mandatory error - // string by reporting the error from the second check. - result = mandatory_result; - } + return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("mempool-script-verify-flag-failed (%s)", ScriptErrorString(result->first)), result->second); + } else { + return state.Invalid(TxValidationResult::TX_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(result->first)), result->second); } - - // MANDATORY flag failures correspond to - // TxValidationResult::TX_CONSENSUS. - return state.Invalid(TxValidationResult::TX_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(result->first)), result->second); } } diff --git a/test/functional/data/invalid_txs.py b/test/functional/data/invalid_txs.py index 32a188d19bf..3c249b2fb8c 100644 --- a/test/functional/data/invalid_txs.py +++ b/test/functional/data/invalid_txs.py @@ -223,7 +223,7 @@ class CreateSumTooLarge(BadTxTemplate): class InvalidOPIFConstruction(BadTxTemplate): - reject_reason = "mandatory-script-verify-flag-failed (Invalid OP_IF construction)" + reject_reason = "mempool-script-verify-flag-failed (Invalid OP_IF construction)" expect_disconnect = False def get_tx(self): @@ -276,8 +276,9 @@ def getDisabledOpcodeTemplate(opcode): }) class NonStandardAndInvalid(BadTxTemplate): - """A non-standard transaction which is also consensus-invalid should return the consensus error.""" - reject_reason = "mandatory-script-verify-flag-failed (OP_RETURN was encountered)" + """A non-standard transaction which is also consensus-invalid should return the first error.""" + reject_reason = "mempool-script-verify-flag-failed (Using OP_CODESEPARATOR in non-witness script)" + block_reject_reason = "mandatory-script-verify-flag-failed (OP_RETURN was encountered)" expect_disconnect = False valid_in_block = False diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 63592122be8..bc0364a8204 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -194,9 +194,12 @@ class FullBlockTest(BitcoinTestFramework): if TxTemplate != invalid_txs.InputMissing: self.sign_tx(badtx, attempt_spend_tx) badblock = self.update_block(blockname, [badtx]) + reject_reason = (template.block_reject_reason or template.reject_reason) + if reject_reason.startswith("mempool-script-verify-flag-failed"): + reject_reason = "mandatory-script-verify-flag-failed" + reject_reason[33:] self.send_blocks( [badblock], success=False, - reject_reason=(template.block_reject_reason or template.reject_reason), + reject_reason=reject_reason, reconnect=True, timeout=2) self.move_tip(2) diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index 2f34f557080..75cd38a98bf 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -153,12 +153,14 @@ class BIP65Test(BitcoinTestFramework): coin_vout = coin.prevout.n cltv_invalidate(spendtx, i) + blk_rej = "mandatory-script-verify-flag-failed" + tx_rej = "mempool-script-verify-flag-failed" expected_cltv_reject_reason = [ - "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", - "mandatory-script-verify-flag-failed (Negative locktime)", - "mandatory-script-verify-flag-failed (Locktime requirement not satisfied)", - "mandatory-script-verify-flag-failed (Locktime requirement not satisfied)", - "mandatory-script-verify-flag-failed (Locktime requirement not satisfied)", + " (Operation not valid with the current stack size)", + " (Negative locktime)", + " (Locktime requirement not satisfied)", + " (Locktime requirement not satisfied)", + " (Locktime requirement not satisfied)", ][i] # First we show that this tx is valid except for CLTV by getting it # rejected from the mempool for exactly that reason. @@ -169,8 +171,8 @@ class BIP65Test(BitcoinTestFramework): 'txid': spendtx_txid, 'wtxid': spendtx_wtxid, 'allowed': False, - 'reject-reason': expected_cltv_reject_reason, - 'reject-details': expected_cltv_reject_reason + f", input 0 of {spendtx_txid} (wtxid {spendtx_wtxid}), spending {coin_txid}:{coin_vout}" + 'reject-reason': tx_rej + expected_cltv_reject_reason, + 'reject-details': tx_rej + expected_cltv_reject_reason + f", input 0 of {spendtx_txid} (wtxid {spendtx_wtxid}), spending {coin_txid}:{coin_vout}" }], self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0), ) @@ -180,7 +182,7 @@ class BIP65Test(BitcoinTestFramework): block.hashMerkleRoot = block.calc_merkle_root() block.solve() - with self.nodes[0].assert_debug_log(expected_msgs=[f'Block validation error: {expected_cltv_reject_reason}']): + with self.nodes[0].assert_debug_log(expected_msgs=[f'Block validation error: {blk_rej + expected_cltv_reject_reason}']): peer.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) peer.sync_with_ping() diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index e0e004675cd..eec1559a13e 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -121,8 +121,8 @@ class BIP66Test(BitcoinTestFramework): 'txid': spendtx_txid, 'wtxid': spendtx_wtxid, 'allowed': False, - 'reject-reason': 'mandatory-script-verify-flag-failed (Non-canonical DER signature)', - 'reject-details': 'mandatory-script-verify-flag-failed (Non-canonical DER signature), ' + + 'reject-reason': 'mempool-script-verify-flag-failed (Non-canonical DER signature)', + 'reject-details': 'mempool-script-verify-flag-failed (Non-canonical DER signature), ' + f"input 0 of {spendtx_txid} (wtxid {spendtx_wtxid}), spending {coin_txid}:0" }], self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0), diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py index f301d65a702..cd7c4491a27 100755 --- a/test/functional/feature_nulldummy.py +++ b/test/functional/feature_nulldummy.py @@ -37,8 +37,8 @@ from test_framework.util import ( from test_framework.wallet import getnewdestination from test_framework.wallet_util import generate_keypair -NULLDUMMY_ERROR = "mandatory-script-verify-flag-failed (Dummy CHECKMULTISIG argument must be zero)" - +NULLDUMMY_TX_ERROR = "mempool-script-verify-flag-failed (Dummy CHECKMULTISIG argument must be zero)" +NULLDUMMY_BLK_ERROR = "mandatory-script-verify-flag-failed (Dummy CHECKMULTISIG argument must be zero)" def invalidate_nulldummy_tx(tx): """Transform a NULLDUMMY compliant tx (i.e. scriptSig starts with OP_0) @@ -104,7 +104,7 @@ class NULLDUMMYTest(BitcoinTestFramework): addr=self.ms_address, amount=47, privkey=self.privkey) invalidate_nulldummy_tx(test2tx) - assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test2tx.serialize_with_witness().hex(), 0) + assert_raises_rpc_error(-26, NULLDUMMY_TX_ERROR, self.nodes[0].sendrawtransaction, test2tx.serialize_with_witness().hex(), 0) self.log.info(f"Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [{COINBASE_MATURITY + 4}]") self.block_submit(self.nodes[0], [test2tx], accept=True) @@ -115,7 +115,7 @@ class NULLDUMMYTest(BitcoinTestFramework): privkey=self.privkey) test6txs = [CTransaction(test4tx)] invalidate_nulldummy_tx(test4tx) - assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test4tx.serialize_with_witness().hex(), 0) + assert_raises_rpc_error(-26, NULLDUMMY_TX_ERROR, self.nodes[0].sendrawtransaction, test4tx.serialize_with_witness().hex(), 0) self.block_submit(self.nodes[0], [test4tx], accept=False) self.log.info("Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation") @@ -125,7 +125,7 @@ class NULLDUMMYTest(BitcoinTestFramework): privkey=self.privkey) test6txs.append(CTransaction(test5tx)) test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01' - assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test5tx.serialize_with_witness().hex(), 0) + assert_raises_rpc_error(-26, NULLDUMMY_TX_ERROR, self.nodes[0].sendrawtransaction, test5tx.serialize_with_witness().hex(), 0) self.block_submit(self.nodes[0], [test5tx], with_witness=True, accept=False) self.log.info(f"Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [{COINBASE_MATURITY + 5}]") @@ -141,7 +141,7 @@ class NULLDUMMYTest(BitcoinTestFramework): if with_witness: add_witness_commitment(block) block.solve() - assert_equal(None if accept else NULLDUMMY_ERROR, node.submitblock(block.serialize().hex())) + assert_equal(None if accept else NULLDUMMY_BLK_ERROR, node.submitblock(block.serialize().hex())) if accept: assert_equal(node.getbestblockhash(), block.hash_hex) self.lastblockhash = block.hash_hex diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index 967b9e1e6eb..80266aa2e72 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -177,8 +177,8 @@ class SegWitTest(BitcoinTestFramework): assert_equal(self.nodes[2].getbalance(), 20 * Decimal("49.999")) self.log.info("Verify unsigned p2sh witness txs without a redeem script are invalid") - self.fail_accept(self.nodes[2], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WPKH][1], sign=False) - self.fail_accept(self.nodes[2], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WSH][1], sign=False) + self.fail_accept(self.nodes[2], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WPKH][1], sign=False) + self.fail_accept(self.nodes[2], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WSH][1], sign=False) self.generate(self.nodes[0], 1) # block 164 @@ -197,13 +197,13 @@ class SegWitTest(BitcoinTestFramework): self.log.info("Verify default node can't accept txs with missing witness") # unsigned, no scriptsig - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag-failed (Witness program hash mismatch)", wit_ids[NODE_0][P2WPKH][0], sign=False) - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag-failed (Witness program was passed an empty witness)", wit_ids[NODE_0][P2WSH][0], sign=False) - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WPKH][0], sign=False) - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WSH][0], sign=False) + self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program hash mismatch)", wit_ids[NODE_0][P2WPKH][0], sign=False) + self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program was passed an empty witness)", wit_ids[NODE_0][P2WSH][0], sign=False) + self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WPKH][0], sign=False) + self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WSH][0], sign=False) # unsigned with redeem script - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag-failed (Witness program hash mismatch)", p2sh_ids[NODE_0][P2WPKH][0], sign=False, redeem_script=witness_script(False, self.pubkey[0])) - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag-failed (Witness program was passed an empty witness)", p2sh_ids[NODE_0][P2WSH][0], sign=False, redeem_script=witness_script(True, self.pubkey[0])) + self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program hash mismatch)", p2sh_ids[NODE_0][P2WPKH][0], sign=False, redeem_script=witness_script(False, self.pubkey[0])) + self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program was passed an empty witness)", p2sh_ids[NODE_0][P2WSH][0], sign=False, redeem_script=witness_script(True, self.pubkey[0])) # Coinbase contains the witness commitment nonce, check that RPC shows us coinbase_txid = self.nodes[2].getblock(blockhash)['tx'][0] @@ -214,10 +214,10 @@ class SegWitTest(BitcoinTestFramework): assert_equal(witnesses[0], '00' * 32) self.log.info("Verify witness txs without witness data are invalid after the fork") - self.fail_accept(self.nodes[2], 'mandatory-script-verify-flag-failed (Witness program hash mismatch)', wit_ids[NODE_2][P2WPKH][2], sign=False) - self.fail_accept(self.nodes[2], 'mandatory-script-verify-flag-failed (Witness program was passed an empty witness)', wit_ids[NODE_2][P2WSH][2], sign=False) - self.fail_accept(self.nodes[2], 'mandatory-script-verify-flag-failed (Witness program hash mismatch)', p2sh_ids[NODE_2][P2WPKH][2], sign=False, redeem_script=witness_script(False, self.pubkey[2])) - self.fail_accept(self.nodes[2], 'mandatory-script-verify-flag-failed (Witness program was passed an empty witness)', p2sh_ids[NODE_2][P2WSH][2], sign=False, redeem_script=witness_script(True, self.pubkey[2])) + self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program hash mismatch)', wit_ids[NODE_2][P2WPKH][2], sign=False) + self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program was passed an empty witness)', wit_ids[NODE_2][P2WSH][2], sign=False) + self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program hash mismatch)', p2sh_ids[NODE_2][P2WPKH][2], sign=False, redeem_script=witness_script(False, self.pubkey[2])) + self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program was passed an empty witness)', p2sh_ids[NODE_2][P2WSH][2], sign=False, redeem_script=witness_script(True, self.pubkey[2])) self.log.info("Verify default node can now use witness txs") self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WPKH][0], True) diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py index e225b68cc10..867c9312208 100755 --- a/test/functional/mempool_accept.py +++ b/test/functional/mempool_accept.py @@ -475,7 +475,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework): nested_anchor_spend.vout.append(CTxOut(nested_anchor_tx.vout[0].nValue - int(fee*COIN), script_to_p2wsh_script(CScript([OP_TRUE])))) self.check_mempool_result( - result_expected=[{'txid': nested_anchor_spend.txid_hex, 'allowed': False, 'reject-reason': 'non-mandatory-script-verify-flag (Witness version reserved for soft-fork upgrades)'}], + result_expected=[{'txid': nested_anchor_spend.txid_hex, 'allowed': False, 'reject-reason': 'mempool-script-verify-flag-failed (Witness version reserved for soft-fork upgrades)'}], rawtxs=[nested_anchor_spend.serialize().hex()], maxfeerate=0, ) diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index 8193ff7cd69..810b6a47ce4 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -690,19 +690,19 @@ class SegWitTest(BitcoinTestFramework): # segwit activation. Note that older bitcoind's that are not # segwit-aware would also reject this for failing CLEANSTACK. with self.nodes[0].assert_debug_log( - expected_msgs=[spend_tx.txid_hex, 'was not accepted: mandatory-script-verify-flag-failed (Witness program was passed an empty witness)']): + expected_msgs=[spend_tx.txid_hex, 'was not accepted: mempool-script-verify-flag-failed (Witness program was passed an empty witness)']): test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False) # The transaction was detected as witness stripped above and not added to the reject # filter. Trying again will check it again and result in the same error. with self.nodes[0].assert_debug_log( - expected_msgs=[spend_tx.txid_hex, 'was not accepted: mandatory-script-verify-flag-failed (Witness program was passed an empty witness)']): + expected_msgs=[spend_tx.txid_hex, 'was not accepted: mempool-script-verify-flag-failed (Witness program was passed an empty witness)']): test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False) # Try to put the witness script in the scriptSig, should also fail. spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a']) with self.nodes[0].assert_debug_log( - expected_msgs=[spend_tx.txid_hex, 'was not accepted: mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)']): + expected_msgs=[spend_tx.txid_hex, 'was not accepted: mempool-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)']): test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False) # Now put the witness script in the witness, should succeed after @@ -1254,7 +1254,7 @@ class SegWitTest(BitcoinTestFramework): # Now do the opposite: strip the witness entirely. This will be detected as witness stripping and # the (w)txid won't be added to the reject filter: we can try again and get the same error. tx3.wit.vtxinwit[0].scriptWitness.stack = [] - reason = "was not accepted: mandatory-script-verify-flag-failed (Witness program was passed an empty witness)" + reason = "was not accepted: mempool-script-verify-flag-failed (Witness program was passed an empty witness)" test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=False, accepted=False, reason=reason) test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=False, accepted=False, reason=reason) @@ -1447,7 +1447,7 @@ class SegWitTest(BitcoinTestFramework): sign_input_segwitv0(tx2, 0, script, tx.vout[0].nValue, key) # Should fail policy test. - test_transaction_acceptance(self.nodes[0], self.test_node, tx2, True, False, 'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + test_transaction_acceptance(self.nodes[0], self.test_node, tx2, True, False, 'mempool-script-verify-flag-failed (Using non-compressed keys in segwit)') # But passes consensus. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx2]) @@ -1466,7 +1466,7 @@ class SegWitTest(BitcoinTestFramework): sign_p2pk_witness_input(witness_script, tx3, 0, SIGHASH_ALL, tx2.vout[0].nValue, key) # Should fail policy test. - test_transaction_acceptance(self.nodes[0], self.test_node, tx3, True, False, 'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + test_transaction_acceptance(self.nodes[0], self.test_node, tx3, True, False, 'mempool-script-verify-flag-failed (Using non-compressed keys in segwit)') # But passes consensus. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx3]) @@ -1483,7 +1483,7 @@ class SegWitTest(BitcoinTestFramework): sign_p2pk_witness_input(witness_script, tx4, 0, SIGHASH_ALL, tx3.vout[0].nValue, key) # Should fail policy test. - test_transaction_acceptance(self.nodes[0], self.test_node, tx4, True, False, 'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + test_transaction_acceptance(self.nodes[0], self.test_node, tx4, True, False, 'mempool-script-verify-flag-failed (Using non-compressed keys in segwit)') block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx4]) test_witness_block(self.nodes[0], self.test_node, block, accepted=True) diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py index 3c467394cfd..325a29c4efc 100755 --- a/test/functional/rpc_packages.py +++ b/test/functional/rpc_packages.py @@ -123,8 +123,8 @@ class RPCPackagesTest(BitcoinTestFramework): assert_equal(testres_bad_sig, self.independent_txns_testres + [{ "txid": tx_bad_sig_txid, "wtxid": tx_bad_sig_wtxid, "allowed": False, - "reject-reason": "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", - "reject-details": "mandatory-script-verify-flag-failed (Operation not valid with the current stack size), " + + "reject-reason": "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", + "reject-details": "mempool-script-verify-flag-failed (Operation not valid with the current stack size), " + f"input 0 of {tx_bad_sig_txid} (wtxid {tx_bad_sig_wtxid}), spending {coin['txid']}:{coin['vout']}" }])