From 9a04635432183c437829339dbf10e7d702581010 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Fri, 8 Aug 2025 12:57:41 -0400 Subject: [PATCH 1/3] scripted-diff: validation: rename mandatory errors into block errors Using "block" or "mempool" as the prefix in place of "mandatory" or "non-mandatory" is clearer to a user. "non-mandatory" was renamed into "mempool" as part of #33050. This takes care of the other half of this renaming as a scripted diff. -BEGIN VERIFY SCRIPT- sed -i 's/mandatory-script-verify/block-script-verify/g' $(git grep -l mandatory-script-verify) -END VERIFY SCRIPT- --- contrib/tracing/mempool_monitor.py | 2 +- src/test/miner_tests.cpp | 2 +- src/validation.cpp | 4 ++-- test/functional/data/invalid_txs.py | 2 +- test/functional/feature_block.py | 2 +- test/functional/feature_cltv.py | 2 +- test/functional/feature_csv_activation.py | 16 ++++++++-------- test/functional/feature_dersig.py | 2 +- test/functional/feature_nulldummy.py | 2 +- test/functional/interface_usdt_mempool.py | 2 +- test/functional/p2p_segwit.py | 20 ++++++++++---------- 11 files changed, 28 insertions(+), 28 deletions(-) diff --git a/contrib/tracing/mempool_monitor.py b/contrib/tracing/mempool_monitor.py index eb29b374158..ddba9d041ff 100755 --- a/contrib/tracing/mempool_monitor.py +++ b/contrib/tracing/mempool_monitor.py @@ -18,7 +18,7 @@ PROGRAM = """ # include // The longest rejection reason is 118 chars and is generated in case of SCRIPT_ERR_EVAL_FALSE by -// strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())) +// strprintf("block-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())) #define MAX_REJECT_REASON_LENGTH 118 // The longest string returned by RemovalReasonToString() is 'sizelimit' #define MAX_REMOVAL_REASON_LENGTH 9 diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 9207f1bfc2c..8729e42e5f1 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -446,7 +446,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std:: tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); AddToMempool(tx_mempool, entry.Fee(LOWFEE).Time(Now()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_EXCEPTION(mining->createNewBlock(options), std::runtime_error, HasReason("mandatory-script-verify-flag-failed")); + BOOST_CHECK_EXCEPTION(mining->createNewBlock(options), std::runtime_error, HasReason("block-script-verify-flag-failed")); // Delete the dummy blocks again. while (m_node.chainman->ActiveChain().Tip()->nHeight > nHeight) { diff --git a/src/validation.cpp b/src/validation.cpp index 2a680b1c7f4..8bba39965f1 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2200,7 +2200,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state, if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { 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); + return state.Invalid(TxValidationResult::TX_CONSENSUS, strprintf("block-script-verify-flag-failed (%s)", ScriptErrorString(result->first)), result->second); } } } @@ -2674,7 +2674,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, if (control) { auto parallel_result = control->Complete(); if (parallel_result.has_value() && state.IsValid()) { - state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(parallel_result->first)), parallel_result->second); + state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, strprintf("block-script-verify-flag-failed (%s)", ScriptErrorString(parallel_result->first)), parallel_result->second); } } if (!state.IsValid()) { diff --git a/test/functional/data/invalid_txs.py b/test/functional/data/invalid_txs.py index a283945a6bd..a7cdcb058fe 100644 --- a/test/functional/data/invalid_txs.py +++ b/test/functional/data/invalid_txs.py @@ -260,7 +260,7 @@ def getDisabledOpcodeTemplate(opcode): class NonStandardAndInvalid(BadTxTemplate): """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)" + block_reject_reason = "block-script-verify-flag-failed (OP_RETURN was encountered)" valid_in_block = False def get_tx(self): diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 63bee49585b..34141a2cc0a 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -191,7 +191,7 @@ class FullBlockTest(BitcoinTestFramework): 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:] + reject_reason = "block-script-verify-flag-failed" + reject_reason[33:] self.send_blocks( [badblock], success=False, reject_reason=reject_reason, diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index 75cd38a98bf..2a958e9dfca 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -153,7 +153,7 @@ class BIP65Test(BitcoinTestFramework): coin_vout = coin.prevout.n cltv_invalidate(spendtx, i) - blk_rej = "mandatory-script-verify-flag-failed" + blk_rej = "block-script-verify-flag-failed" tx_rej = "mempool-script-verify-flag-failed" expected_cltv_reject_reason = [ " (Operation not valid with the current stack size)", diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index 2abfa84300f..bef870bed8b 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -403,9 +403,9 @@ class BIP68_112_113Test(BitcoinTestFramework): # -1 OP_CSV tx and (empty stack) OP_CSV tx should fail self.send_blocks([self.create_test_block([bip112tx_special_v1])], success=False, - reject_reason='mandatory-script-verify-flag-failed (Negative locktime)') + reject_reason='block-script-verify-flag-failed (Negative locktime)') self.send_blocks([self.create_test_block([bip112tx_emptystack_v1])], success=False, - reject_reason='mandatory-script-verify-flag-failed (Operation not valid with the current stack size)') + reject_reason='block-script-verify-flag-failed (Operation not valid with the current stack size)') # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 1 txs should still pass success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v1 if tx['sdf']] @@ -420,15 +420,15 @@ class BIP68_112_113Test(BitcoinTestFramework): fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v1 if not tx['sdf']] for tx in fail_txs: self.send_blocks([self.create_test_block([tx])], success=False, - reject_reason='mandatory-script-verify-flag-failed (Locktime requirement not satisfied)') + reject_reason='block-script-verify-flag-failed (Locktime requirement not satisfied)') self.log.info("Test version 2 txs") # -1 OP_CSV tx and (empty stack) OP_CSV tx should fail self.send_blocks([self.create_test_block([bip112tx_special_v2])], success=False, - reject_reason='mandatory-script-verify-flag-failed (Negative locktime)') + reject_reason='block-script-verify-flag-failed (Negative locktime)') self.send_blocks([self.create_test_block([bip112tx_emptystack_v2])], success=False, - reject_reason='mandatory-script-verify-flag-failed (Operation not valid with the current stack size)') + reject_reason='block-script-verify-flag-failed (Operation not valid with the current stack size)') # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 2 txs should pass (all sequence locks are met) success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if tx['sdf']] @@ -444,20 +444,20 @@ class BIP68_112_113Test(BitcoinTestFramework): fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_9_v2 if not tx['sdf']] for tx in fail_txs: self.send_blocks([self.create_test_block([tx])], success=False, - reject_reason='mandatory-script-verify-flag-failed (Locktime requirement not satisfied)') + reject_reason='block-script-verify-flag-failed (Locktime requirement not satisfied)') # If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in nSequence, tx should fail fail_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if tx['sdf']] for tx in fail_txs: self.send_blocks([self.create_test_block([tx])], success=False, - reject_reason='mandatory-script-verify-flag-failed (Locktime requirement not satisfied)') + reject_reason='block-script-verify-flag-failed (Locktime requirement not satisfied)') # If sequencelock types mismatch, tx should fail fail_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if not tx['sdf'] and tx['stf']] fail_txs += [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']] for tx in fail_txs: self.send_blocks([self.create_test_block([tx])], success=False, - reject_reason='mandatory-script-verify-flag-failed (Locktime requirement not satisfied)') + reject_reason='block-script-verify-flag-failed (Locktime requirement not satisfied)') # Remaining txs should pass, just test masking works properly success_txs = [tx['tx'] for tx in bip112txs_vary_nSequence_v2 if not tx['sdf'] and not tx['stf']] diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index eec1559a13e..de6d1c09e90 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -133,7 +133,7 @@ class BIP66Test(BitcoinTestFramework): block.hashMerkleRoot = block.calc_merkle_root() block.solve() - with self.nodes[0].assert_debug_log(expected_msgs=['Block validation error: mandatory-script-verify-flag-failed (Non-canonical DER signature)']): + with self.nodes[0].assert_debug_log(expected_msgs=['Block validation error: block-script-verify-flag-failed (Non-canonical DER signature)']): 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_nulldummy.py b/test/functional/feature_nulldummy.py index cd7c4491a27..95760e6a58b 100755 --- a/test/functional/feature_nulldummy.py +++ b/test/functional/feature_nulldummy.py @@ -38,7 +38,7 @@ from test_framework.wallet import getnewdestination from test_framework.wallet_util import generate_keypair 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)" +NULLDUMMY_BLK_ERROR = "block-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) diff --git a/test/functional/interface_usdt_mempool.py b/test/functional/interface_usdt_mempool.py index b69dd2df114..7cf936ab094 100755 --- a/test/functional/interface_usdt_mempool.py +++ b/test/functional/interface_usdt_mempool.py @@ -30,7 +30,7 @@ MEMPOOL_TRACEPOINTS_PROGRAM = """ # include // The longest rejection reason is 118 chars and is generated in case of SCRIPT_ERR_EVAL_FALSE by -// strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())) +// strprintf("block-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())) #define MAX_REJECT_REASON_LENGTH 118 // The longest string returned by RemovalReasonToString() is 'sizelimit' #define MAX_REMOVAL_REASON_LENGTH 9 diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index 810b6a47ce4..8a61eaf3be1 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -505,7 +505,7 @@ class SegWitTest(BitcoinTestFramework): # invalid (transactions are always validated with SCRIPT_VERIFY_WITNESS so a segwit v0 transaction # without a witness is invalid). test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False, - reason='mandatory-script-verify-flag-failed (Witness program was passed an empty witness)') + reason='block-script-verify-flag-failed (Witness program was passed an empty witness)') self.utxo.pop(0) self.utxo.append(UTXO(txid, 2, value)) @@ -977,7 +977,7 @@ class SegWitTest(BitcoinTestFramework): # Extra witness data should not be allowed. test_witness_block(self.nodes[0], self.test_node, block, accepted=False, - reason='mandatory-script-verify-flag-failed (Witness provided for non-witness script)') + reason='block-script-verify-flag-failed (Witness provided for non-witness script)') # Try extra signature data. Ok if we're not spending a witness output. block.vtx[1].wit.vtxinwit = [] @@ -1002,7 +1002,7 @@ class SegWitTest(BitcoinTestFramework): # This has extra witness data, so it should fail. test_witness_block(self.nodes[0], self.test_node, block, accepted=False, - reason='mandatory-script-verify-flag-failed (Stack size must be exactly one after execution)') + reason='block-script-verify-flag-failed (Stack size must be exactly one after execution)') # Now get rid of the extra witness, but add extra scriptSig data tx2.vin[0].scriptSig = CScript([OP_TRUE]) @@ -1014,7 +1014,7 @@ class SegWitTest(BitcoinTestFramework): # This has extra signature data for a witness input, so it should fail. test_witness_block(self.nodes[0], self.test_node, block, accepted=False, - reason='mandatory-script-verify-flag-failed (Witness requires empty scriptSig)') + reason='block-script-verify-flag-failed (Witness requires empty scriptSig)') # Now get rid of the extra scriptsig on the witness input, and verify # success (even with extra scriptsig data in the non-witness input) @@ -1050,7 +1050,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx, tx2]) test_witness_block(self.nodes[0], self.test_node, block, accepted=False, - reason='mandatory-script-verify-flag-failed (Push value size limit exceeded)') + reason='block-script-verify-flag-failed (Push value size limit exceeded)') # Now reduce the length of the stack element tx2.wit.vtxinwit[0].scriptWitness.stack[0] = b'a' * (MAX_SCRIPT_ELEMENT_SIZE) @@ -1089,7 +1089,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx, tx2]) test_witness_block(self.nodes[0], self.test_node, block, accepted=False, - reason='mandatory-script-verify-flag-failed (Script is too big)') + reason='block-script-verify-flag-failed (Script is too big)') # Try again with one less byte in the witness script witness_script = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 62 + [OP_TRUE]) @@ -1179,7 +1179,7 @@ class SegWitTest(BitcoinTestFramework): block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx2]) test_witness_block(self.nodes[0], self.test_node, block, accepted=False, - reason='mandatory-script-verify-flag-failed (Operation not valid with the current stack size)') + reason='block-script-verify-flag-failed (Operation not valid with the current stack size)') # Fix the broken witness and the block should be accepted. tx2.wit.vtxinwit[5].scriptWitness.stack = [b'a', witness_script] @@ -1535,7 +1535,7 @@ class SegWitTest(BitcoinTestFramework): sign_p2pk_witness_input(witness_script, tx, 0, hashtype, prev_utxo.nValue + 1, key) self.update_witness_block_with_transactions(block, [tx]) test_witness_block(self.nodes[0], self.test_node, block, accepted=False, - reason='mandatory-script-verify-flag-failed (Script evaluated without error ' + reason='block-script-verify-flag-failed (Script evaluated without error ' 'but finished with a false/empty top stack element') # Too-small input value @@ -1543,7 +1543,7 @@ class SegWitTest(BitcoinTestFramework): block.vtx.pop() # remove last tx self.update_witness_block_with_transactions(block, [tx]) test_witness_block(self.nodes[0], self.test_node, block, accepted=False, - reason='mandatory-script-verify-flag-failed (Script evaluated without error ' + reason='block-script-verify-flag-failed (Script evaluated without error ' 'but finished with a false/empty top stack element') # Now try correct value @@ -1647,7 +1647,7 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx, tx2]) test_witness_block(self.nodes[0], self.test_node, block, accepted=False, - reason='mandatory-script-verify-flag-failed (Witness requires empty scriptSig)') + reason='block-script-verify-flag-failed (Witness requires empty scriptSig)') # Move the signature to the witness. block.vtx.pop() From b3f781a0ef4b763ef7ba8b5b20871a7707ec090e Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Fri, 8 Aug 2025 13:16:15 -0400 Subject: [PATCH 2/3] contrib: adapt max reject string size in tracing demo The Script errors were last touched in 2020. This value was calculated after that in 2022 (commit 4b7aec2951fe4595946cdc804b0dec1921d79d05). The previous commit made the size of the largest reject reason string 4 characters smaller ("mandatory" became "block"), so adapt the constant. --- contrib/tracing/mempool_monitor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/tracing/mempool_monitor.py b/contrib/tracing/mempool_monitor.py index ddba9d041ff..c5f672fb954 100755 --- a/contrib/tracing/mempool_monitor.py +++ b/contrib/tracing/mempool_monitor.py @@ -17,9 +17,9 @@ from bcc import BPF, USDT PROGRAM = """ # include -// The longest rejection reason is 118 chars and is generated in case of SCRIPT_ERR_EVAL_FALSE by +// The longest rejection reason is 114 chars and is generated in case of SCRIPT_ERR_EVAL_FALSE by // strprintf("block-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())) -#define MAX_REJECT_REASON_LENGTH 118 +#define MAX_REJECT_REASON_LENGTH 114 // The longest string returned by RemovalReasonToString() is 'sizelimit' #define MAX_REMOVAL_REASON_LENGTH 9 #define HASH_LENGTH 32 From c0d91fc69c67e6f7123326d4f3caeac069d2637b Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Thu, 14 Aug 2025 14:05:05 -0400 Subject: [PATCH 3/3] Add release note for #33050 and #33183 error string changes --- doc/release-notes-33183.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 doc/release-notes-33183.md diff --git a/doc/release-notes-33183.md b/doc/release-notes-33183.md new file mode 100644 index 00000000000..fdc3f1fb903 --- /dev/null +++ b/doc/release-notes-33183.md @@ -0,0 +1,7 @@ +Updated RPCs +------------ + +Transaction Script validation errors used to return the reason for the error prefixed by either +"mandatory-script-verify-flag-failed" if it was a consensus error, or "non-mandatory-script-verify-flag" +(without "-failed") if it was a standardness error. This has been changed to "block-script-verify-flag-failed" +and "mempool-script-verify-flag-failed" for all block and mempool errors respectively.