Policy: Report reason inputs are non standard #29060

pull ismaelsadeeq wants to merge 3 commits into bitcoin:master from ismaelsadeeq:11-2023-non-standard-inputs-error-messages changing 10 files +149 −30
  1. ismaelsadeeq commented at 1:16 pm on December 12, 2023: member

    This PR is another attempt at #13525.

    Transactions that fail PreChecks Validation due to non-standard inputs now returns invalid validation stateTxValidationResult::TX_INPUTS_NOT_STANDARD along with a debug error message.

    Previously, the debug error message for non-standard inputs do not specify why the inputs were considered non-standard. Instead, the same error string, bad-txns-nonstandard-inputs, used for all types of non-standard input scriptSigs.

    This PR updates the AreInputsStandard to include the reason why inputs are non-standard in the debug message. This improves the Precheck debug message to be more descriptive.

    Furthermore, I have addressed all remaining comments from #13525 in this PR.

  2. DrahtBot commented at 1:16 pm on December 12, 2023: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/29060.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK stickies-v

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    No conflicts as of last run.

  3. DrahtBot added the label TX fees and policy on Dec 12, 2023
  4. ismaelsadeeq force-pushed on Dec 12, 2023
  5. DrahtBot added the label CI failed on Dec 12, 2023
  6. DrahtBot removed the label CI failed on Dec 12, 2023
  7. in src/policy/policy.cpp:178 in 6ffb9569b7 outdated
    174@@ -174,38 +175,49 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
    175  *
    176  * Note that only the non-witness portion of the transaction is checked here.
    177  */
    178-bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
    179+std::optional<NonStandardInputsReason> AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
    


    stickies-v commented at 1:42 pm on December 13, 2023:

    A function called AreInputsStandard that returns a falsy value if inputs are standard is counterintuitive. Would suggest renaming to e.g. HasNonStandardInput.

      0diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
      1index c677fb1c16..9a31cecd7a 100644
      2--- a/src/bench/ccoins_caching.cpp
      3+++ b/src/bench/ccoins_caching.cpp
      4@@ -44,7 +44,7 @@ static void CCoinsCaching(benchmark::Bench& bench)
      5     // Benchmark.
      6     const CTransaction tx_1(t1);
      7     bench.run([&] {
      8-        auto result{AreInputsStandard(tx_1, coins)};
      9+        auto result{HasNonStandardInput(tx_1, coins)};
     10         assert(!result.has_value());
     11     });
     12     ECC_Stop();
     13diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
     14index c093cb3923..aada94d909 100644
     15--- a/src/policy/policy.cpp
     16+++ b/src/policy/policy.cpp
     17@@ -175,7 +175,7 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
     18  *
     19  * Note that only the non-witness portion of the transaction is checked here.
     20  */
     21-std::optional<NonStandardInputsReason> AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
     22+std::optional<NonStandardInputsReason> HasNonStandardInput(const CTransaction& tx, const CCoinsViewCache& mapInputs)
     23 {
     24     if (tx.IsCoinBase()) {
     25         return std::nullopt; // Coinbases don't use vin normally
     26@@ -187,32 +187,27 @@ std::optional<NonStandardInputsReason> AreInputsStandard(const CTransaction& tx,
     27         std::vector<std::vector<unsigned char> > vSolutions;
     28         TxoutType whichType = Solver(prev.scriptPubKey, vSolutions);
     29         if (whichType == TxoutType::NONSTANDARD) {
     30-            auto result = NonStandardInputsReason{"bad-txns-input-script-nonstandard", strprintf("input %u", i)};
     31-            return result;
     32+            return NonStandardInputsReason{"bad-txns-input-script-nonstandard", strprintf("input %u", i)};
     33         } else if (whichType == TxoutType::WITNESS_UNKNOWN) {
     34             // WITNESS_UNKNOWN failures are typically also caught with a policy
     35             // flag in the script interpreter, but it can be helpful to catch
     36             // this type of NONSTANDARD transaction earlier in transaction
     37             // validation.
     38-            auto result = NonStandardInputsReason{"bad-txns-input-witness-unknown", strprintf("input %u", i)};
     39-            return result;
     40+            return NonStandardInputsReason{"bad-txns-input-witness-unknown", strprintf("input %u", i)};
     41         } else if (whichType == TxoutType::SCRIPTHASH) {
     42             std::vector<std::vector<unsigned char> > stack;
     43             ScriptError serror;
     44             // convert the scriptSig into a stack, so we can inspect the redeemScript
     45             if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE, &serror)) {
     46-                auto result = NonStandardInputsReason{"bad-txns-input-scriptsig-failure", strprintf("input %u: %s", i, ScriptErrorString(serror))};
     47-                return result;
     48+                return NonStandardInputsReason{"bad-txns-input-scriptsig-failure", strprintf("input %u: %s", i, ScriptErrorString(serror))};
     49             }
     50             if (stack.empty()) {
     51-                auto result = NonStandardInputsReason{"bad-txns-input-p2sh-no-redeemscript", strprintf("input %u", i)};
     52-                return result;
     53+                return NonStandardInputsReason{"bad-txns-input-p2sh-no-redeemscript", strprintf("input %u", i)};
     54             }
     55             CScript subscript(stack.back().begin(), stack.back().end());
     56             unsigned int sigop_count = subscript.GetSigOpCount(true);
     57             if (sigop_count > MAX_P2SH_SIGOPS) {
     58-                auto result = NonStandardInputsReason{"bad-txns-input-scriptcheck-sigops", strprintf("input %u: %u > %u", i, sigop_count, MAX_P2SH_SIGOPS)};
     59-                return result;
     60+                return NonStandardInputsReason{"bad-txns-input-scriptcheck-sigops", strprintf("input %u: %u > %u", i, sigop_count, MAX_P2SH_SIGOPS)};
     61             }
     62         }
     63     }
     64diff --git a/src/policy/policy.h b/src/policy/policy.h
     65index 3986f7d709..3d7d4b7e5c 100644
     66--- a/src/policy/policy.h
     67+++ b/src/policy/policy.h
     68@@ -149,7 +149,7 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
     69  * [@return](/bitcoin-bitcoin/contributor/return/) std::nullopt if all inputs (scriptSigs) use only standard transaction forms else returns
     70  * NonStandardInputsReason which states why an input is not standard.
     71  */
     72-std::optional<NonStandardInputsReason> AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
     73+std::optional<NonStandardInputsReason> HasNonStandardInput(const CTransaction& tx, const CCoinsViewCache& mapInputs);
     74 /**
     75 * Check if the transaction is over standard P2WSH resources limit:
     76 * 3600bytes witnessScript size, 80bytes per witness stack element, 100 witness stack elements
     77diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
     78index 8f3e357a84..e210b2a13d 100644
     79--- a/src/test/fuzz/coins_view.cpp
     80+++ b/src/test/fuzz/coins_view.cpp
     81@@ -235,7 +235,7 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
     82                 assert(expected_code_path);
     83             },
     84             [&] {
     85-                (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
     86+                (void)HasNonStandardInput(CTransaction{random_mutable_transaction}, coins_view_cache);
     87             },
     88             [&] {
     89                 TxValidationState state;
     90diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
     91index 2a043f7458..785c823e37 100644
     92--- a/src/test/fuzz/transaction.cpp
     93+++ b/src/test/fuzz/transaction.cpp
     94@@ -87,7 +87,7 @@ FUZZ_TARGET(transaction, .init = initialize_transaction)
     95 
     96     CCoinsView coins_view;
     97     const CCoinsViewCache coins_view_cache(&coins_view);
     98-    (void)AreInputsStandard(tx, coins_view_cache);
     99+    (void)HasNonStandardInput(tx, coins_view_cache);
    100     (void)IsWitnessStandard(tx, coins_view_cache);
    101 
    102     if (tx.GetTotalSize() < 250'000) { // Avoid high memory usage (with msan) due to json encoding
    103diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
    104index 500ec0bd6b..32907bdd24 100644
    105--- a/src/test/script_p2sh_tests.cpp
    106+++ b/src/test/script_p2sh_tests.cpp
    107@@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(switchover)
    108     BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EQUALVERIFY, ScriptErrorString(err));
    109 }
    110 
    111-BOOST_AUTO_TEST_CASE(AreInputsStandard)
    112+BOOST_AUTO_TEST_CASE(GetFirstNonStandardInput)
    113 {
    114     CCoinsView coinsDummy;
    115     CCoinsViewCache coins(&coinsDummy);
    116@@ -366,7 +366,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
    117     txTo.vin[3].scriptSig << OP_11 << OP_11 << std::vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end());
    118     txTo.vin[4].scriptSig << std::vector<unsigned char>(fifteenSigops.begin(), fifteenSigops.end());
    119 
    120-    BOOST_CHECK(!::AreInputsStandard(CTransaction(txTo), coins).has_value());
    121+    BOOST_CHECK(!::HasNonStandardInput(CTransaction(txTo), coins).has_value());
    122     // 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4]
    123     BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txTo), coins), 22U);
    124 
    125@@ -380,7 +380,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
    126     txToNonStd1.vin[0].prevout.hash = txFrom.GetHash();
    127     txToNonStd1.vin[0].scriptSig << std::vector<unsigned char>(sixteenSigops.begin(), sixteenSigops.end());
    128 
    129-    const auto txToNonStd1_res = ::AreInputsStandard(CTransaction(txToNonStd1), coins);
    130+    const auto txToNonStd1_res = ::HasNonStandardInput(CTransaction(txToNonStd1), coins);
    131     BOOST_CHECK(txToNonStd1_res.has_value());
    132     BOOST_CHECK_EQUAL(txToNonStd1_res->reason, "bad-txns-input-scriptcheck-sigops");
    133     BOOST_CHECK_EQUAL(txToNonStd1_res->debug, "input 0: 16 > 15");
    134@@ -398,7 +398,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
    135 
    136     std::vector<std::vector<unsigned char>> vSolutions;
    137     BOOST_CHECK_EQUAL(Solver(txFrom.vout[6].scriptPubKey, vSolutions), TxoutType::SCRIPTHASH);
    138-    const auto txToNonStd2_res = ::AreInputsStandard(CTransaction(txToNonStd2), coins);
    139+    const auto txToNonStd2_res = ::HasNonStandardInput(CTransaction(txToNonStd2), coins);
    140     BOOST_CHECK(txToNonStd2_res.has_value());
    141     BOOST_CHECK_EQUAL(txToNonStd2_res->reason, "bad-txns-input-scriptcheck-sigops");
    142     BOOST_CHECK_EQUAL(txToNonStd2_res->debug, "input 0: 20 > 15");
    143@@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
    144     txToNonStd2_no_scriptSig.vin[0].prevout.hash = txFrom.GetHash();
    145 
    146     BOOST_CHECK_EQUAL(Solver(txFrom.vout[6].scriptPubKey, vSolutions), TxoutType::SCRIPTHASH);
    147-    const auto txToNonStd2_no_scriptSig_res = ::AreInputsStandard(CTransaction(txToNonStd2_no_scriptSig), coins);
    148+    const auto txToNonStd2_no_scriptSig_res = ::HasNonStandardInput(CTransaction(txToNonStd2_no_scriptSig), coins);
    149     BOOST_CHECK(txToNonStd2_no_scriptSig_res.has_value());
    150     BOOST_CHECK_EQUAL(txToNonStd2_no_scriptSig_res->reason, "bad-txns-input-p2sh-no-redeemscript");
    151     BOOST_CHECK_EQUAL(txToNonStd2_no_scriptSig_res->debug, "input 0");
    152@@ -429,7 +429,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
    153     txToNonStd3.vin[0].prevout.hash = txFrom.GetHash();
    154 
    155     BOOST_CHECK_EQUAL(Solver(txFrom.vout[7].scriptPubKey, vSolutions), TxoutType::NONSTANDARD);
    156-    const auto txToNonStd3_res = ::AreInputsStandard(CTransaction(txToNonStd3), coins);
    157+    const auto txToNonStd3_res = ::HasNonStandardInput(CTransaction(txToNonStd3), coins);
    158     BOOST_CHECK(txToNonStd3_res.has_value());
    159     BOOST_CHECK_EQUAL(txToNonStd3_res->reason, "bad-txns-input-script-nonstandard");
    160     BOOST_CHECK_EQUAL(txToNonStd3_res->debug, "input 0");
    161@@ -451,7 +451,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
    162     BOOST_CHECK_EQUAL(Solver(txFrom.vout[8].scriptPubKey, vSolutions), TxoutType::SCRIPTHASH);
    163     BOOST_CHECK(!EvalScript(stack, txToNonStd4.vin[0].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE, &serror));
    164     BOOST_CHECK_EQUAL(serror, SCRIPT_ERR_OP_RETURN);
    165-    const auto txToNonStd4_res = ::AreInputsStandard(CTransaction(txToNonStd4), coins);
    166+    const auto txToNonStd4_res = ::HasNonStandardInput(CTransaction(txToNonStd4), coins);
    167     BOOST_CHECK(txToNonStd4_res.has_value());
    168     BOOST_CHECK_EQUAL(txToNonStd4_res->reason, "bad-txns-input-scriptsig-failure");
    169     BOOST_CHECK_EQUAL(txToNonStd4_res->debug, "input 0: OP_RETURN was encountered");
    170@@ -465,7 +465,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
    171     txWitnessUnknown.vin[0].prevout.n = 9;
    172     txWitnessUnknown.vin[0].prevout.hash = txFrom.GetHash();
    173     BOOST_CHECK_EQUAL(Solver(txFrom.vout[9].scriptPubKey, vSolutions), TxoutType::WITNESS_UNKNOWN);
    174-    const auto txWitnessUnknown_res = ::AreInputsStandard(CTransaction(txWitnessUnknown), coins);
    175+    const auto txWitnessUnknown_res = ::HasNonStandardInput(CTransaction(txWitnessUnknown), coins);
    176     BOOST_CHECK(txWitnessUnknown_res.has_value());
    177     BOOST_CHECK_EQUAL(txWitnessUnknown_res->reason, "bad-txns-input-witness-unknown");
    178     BOOST_CHECK_EQUAL(txWitnessUnknown_res->debug, "input 0");
    179diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
    180index e2443a2fce..05074eb631 100644
    181--- a/src/test/transaction_tests.cpp
    182+++ b/src/test/transaction_tests.cpp
    183@@ -406,7 +406,7 @@ BOOST_AUTO_TEST_CASE(test_Get)
    184     t1.vout[0].nValue = 90*CENT;
    185     t1.vout[0].scriptPubKey << OP_1;
    186 
    187-    BOOST_CHECK(!AreInputsStandard(CTransaction(t1), coins).has_value());
    188+    BOOST_CHECK(!HasNonStandardInput(CTransaction(t1), coins).has_value());
    189 }
    190 
    191 static void CreateCreditAndSpend(const FillableSigningProvider& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
    192diff --git a/src/validation.cpp b/src/validation.cpp
    193index 96204b2ab4..cdcf56860f 100644
    194--- a/src/validation.cpp
    195+++ b/src/validation.cpp
    196@@ -822,7 +822,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
    197         return false; // state filled in by CheckTxInputs
    198     }
    199 
    200-    const auto inputs_standardness_result = AreInputsStandard(tx, m_view);
    201+    const auto inputs_standardness_result = HasNonStandardInput(tx, m_view);
    202     if (m_pool.m_require_standard && inputs_standardness_result.has_value()) {
    203         return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, inputs_standardness_result.value().reason, inputs_standardness_result.value().debug);
    204     }
    

    Alternatively, could use util::Result failure values after #25665 is merged.


    stickies-v commented at 3:09 pm on December 13, 2023:
    nit: requires #include <optional> (in both files)

    ismaelsadeeq commented at 11:45 am on December 14, 2023:
    Thank you, HasNonStandardInput makes more sense, rename to HasNonStandardInput in 94cd47f3460

    ismaelsadeeq commented at 11:47 am on December 14, 2023:
    Fixed

    ismaelsadeeq commented at 12:34 pm on December 25, 2023:

    Alternatively, could use util::Result failure values after #25665 is merged.

    will resolve this since I am now using TxValidationState

  8. in src/policy/policy.cpp:191 in 6ffb9569b7 outdated
    189         std::vector<std::vector<unsigned char> > vSolutions;
    190         TxoutType whichType = Solver(prev.scriptPubKey, vSolutions);
    191-        if (whichType == TxoutType::NONSTANDARD || whichType == TxoutType::WITNESS_UNKNOWN) {
    192+        if (whichType == TxoutType::NONSTANDARD) {
    193+            auto result = NonStandardInputsReason{"bad-txns-input-script-nonstandard", strprintf("input %u", i)};
    194+            return result;
    


    stickies-v commented at 1:43 pm on December 13, 2023:

    nit: No need for the intermediary var, here and in a bunch of other places

    0            return NonStandardInputsReason{"bad-txns-input-script-nonstandard", strprintf("input %u", i)};
    

    ismaelsadeeq commented at 11:45 am on December 14, 2023:
    Yes, fixed now
  9. in src/test/script_p2sh_tests.cpp:369 in 6ffb9569b7 outdated
    365@@ -350,10 +366,11 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
    366     txTo.vin[3].scriptSig << OP_11 << OP_11 << std::vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end());
    367     txTo.vin[4].scriptSig << std::vector<unsigned char>(fifteenSigops.begin(), fifteenSigops.end());
    368 
    369-    BOOST_CHECK(::AreInputsStandard(CTransaction(txTo), coins));
    370+    BOOST_CHECK(!::AreInputsStandard(CTransaction(txTo), coins).has_value());
    


    stickies-v commented at 1:44 pm on December 13, 2023:

    nit: no need for .has_value(), idiomatic enough as is imo (here and in a bunch of other places)

    0    BOOST_CHECK(!::AreInputsStandard(CTransaction(txTo), coins));
    

    ismaelsadeeq commented at 11:45 am on December 14, 2023:
    Fixed in latest push
  10. in src/validation.cpp:827 in 6ffb9569b7 outdated
    821@@ -822,8 +822,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
    822         return false; // state filled in by CheckTxInputs
    823     }
    824 
    825-    if (m_pool.m_require_standard && !AreInputsStandard(tx, m_view)) {
    826-        return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
    827+    const auto inputs_standardness_result = AreInputsStandard(tx, m_view);
    828+    if (m_pool.m_require_standard && inputs_standardness_result.has_value()) {
    829+        return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, inputs_standardness_result.value().reason, inputs_standardness_result.value().debug);
    


    stickies-v commented at 1:45 pm on December 13, 2023:

    Since we’ve validated has_value() just above, can just derefence safely for conciseness:

    0        return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, inputs_standardness_result->reason, inputs_standardness_result->debug);
    

    ismaelsadeeq commented at 11:45 am on December 14, 2023:
    Fixed
  11. in src/bench/ccoins_caching.cpp:48 in 6ffb9569b7 outdated
    43@@ -44,8 +44,8 @@ static void CCoinsCaching(benchmark::Bench& bench)
    44     // Benchmark.
    45     const CTransaction tx_1(t1);
    46     bench.run([&] {
    47-        bool success{AreInputsStandard(tx_1, coins)};
    48-        assert(success);
    49+        auto result{AreInputsStandard(tx_1, coins)};
    50+        assert(!result.has_value());
    


    stickies-v commented at 1:46 pm on December 13, 2023:

    nit: more concise, easier to read (+ in other places)

    0        assert(!AreInputsStandard(tx_1, coins));
    

    ismaelsadeeq commented at 11:46 am on December 14, 2023:
    Taken
  12. in src/policy/policy.h:124 in 6ffb9569b7 outdated
    120@@ -121,6 +121,11 @@ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIP
    121 /** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */
    122 static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS{LOCKTIME_VERIFY_SEQUENCE};
    123 
    124+struct NonStandardInputsReason {
    


    stickies-v commented at 2:25 pm on December 13, 2023:

    nit: this is the reason for a single input

    0struct NonStandardInputReason {
    

    ismaelsadeeq commented at 11:46 am on December 14, 2023:
    Yes, taken
  13. in src/validation.cpp:825 in 6ffb9569b7 outdated
    821@@ -822,8 +822,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
    822         return false; // state filled in by CheckTxInputs
    823     }
    824 
    825-    if (m_pool.m_require_standard && !AreInputsStandard(tx, m_view)) {
    826-        return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
    827+    const auto inputs_standardness_result = AreInputsStandard(tx, m_view);
    


    stickies-v commented at 3:07 pm on December 13, 2023:

    This causes unnecessary input validation if !m_require_standard, would suggest changing to e.g.

     0diff --git a/src/validation.cpp b/src/validation.cpp
     1index 96204b2ab4..bf1c578384 100644
     2--- a/src/validation.cpp
     3+++ b/src/validation.cpp
     4@@ -822,9 +822,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
     5         return false; // state filled in by CheckTxInputs
     6     }
     7 
     8-    const auto inputs_standardness_result = AreInputsStandard(tx, m_view);
     9-    if (m_pool.m_require_standard && inputs_standardness_result.has_value()) {
    10-        return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, inputs_standardness_result.value().reason, inputs_standardness_result.value().debug);
    11+    if (m_pool.m_require_standard) {
    12+        if (const auto result{AreInputsStandard(tx, m_view)}) {
    13+            return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, result->reason, result->debug);
    14+        }
    15     }
    16 
    17     // Check for non-standard witnesses.
    

    ismaelsadeeq commented at 11:47 am on December 14, 2023:
    Thank you fixed in 94cd47f3460
  14. stickies-v commented at 3:15 pm on December 13, 2023: contributor
    Concept ACK
  15. ismaelsadeeq force-pushed on Dec 14, 2023
  16. ismaelsadeeq commented at 11:49 am on December 14, 2023: member
  17. in src/policy/policy.cpp:191 in e032462c44 outdated
    189 
    190         std::vector<std::vector<unsigned char> > vSolutions;
    191         TxoutType whichType = Solver(prev.scriptPubKey, vSolutions);
    192-        if (whichType == TxoutType::NONSTANDARD || whichType == TxoutType::WITNESS_UNKNOWN) {
    193+        if (whichType == TxoutType::NONSTANDARD) {
    194+            return NonStandardInputReason{"bad-txns-input-script-nonstandard", strprintf("input %u", i)};
    


    luke-jr commented at 6:09 pm on December 17, 2023:
    0            return NonStandardInputReason{"bad-txns-input-script-unknown", strprintf("input %u", i)};
    

    ismaelsadeeq commented at 2:54 pm on December 18, 2023:
    Fixed thanks
  18. in src/policy/policy.cpp:206 in e032462c44 outdated
    209-                return false;
    210+            if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE, &serror)) {
    211+                return NonStandardInputReason{"bad-txns-input-scriptsig-failure", strprintf("input %u: %s", i, ScriptErrorString(serror))};
    212+            }
    213+            if (stack.empty()) {
    214+                return NonStandardInputReason{"bad-txns-input-p2sh-no-redeemscript", strprintf("input %u", i)};
    


    luke-jr commented at 6:11 pm on December 17, 2023:
    0                return NonStandardInputReason{"bad-txns-input-scriptcheck-missing", strprintf("input %u", i)};
    

    ismaelsadeeq commented at 2:54 pm on December 18, 2023:
    fixed
  19. luke-jr changes_requested
  20. ismaelsadeeq force-pushed on Dec 18, 2023
  21. ismaelsadeeq commented at 3:00 pm on December 18, 2023: member

    Thanks for reviewing @luke-jr Force pushed from https://github.com/bitcoin/bitcoin/commit/e032462c44dba9b6961c1e18bd597d63c6afac02 to https://github.com/bitcoin/bitcoin/pull/29060/commits/0a39b0708ec72292ab3e1dde643dedfbd5d20674

    Compare diff

    The changes in latest push are:

    • Rebased on master to fix CI
    • Updated the non-standardness reason to address @luke-jr comments
    • bad-txns-input-script-nonstandard –> bad-txns-input-script-unknown
    • bad-txns-input-scriptsig-failure –> bad-txns-input-p2sh-scriptsig-malformed
    • bad-txns-input-p2sh-no-redeemscript –> bad-txns-input-scriptcheck-missing
    • bad-txns-input-scriptcheck-sigops –> bad-txns-input-p2sh-redeemscript-sigops
  22. ismaelsadeeq force-pushed on Dec 18, 2023
  23. in src/policy/policy.h:128 in 0a39b0708e outdated
    121@@ -121,6 +122,11 @@ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIP
    122 /** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */
    123 static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS{LOCKTIME_VERIFY_SEQUENCE};
    124 
    125+struct NonStandardInputReason {
    126+    std::string reason;
    127+    std::string debug;
    128+};
    


    glozow commented at 11:06 am on December 20, 2023:
    Maybe just use TxValidationState?

    ismaelsadeeq commented at 4:09 pm on December 24, 2023:
    Thanks updated
  24. ismaelsadeeq force-pushed on Dec 24, 2023
  25. ismaelsadeeq force-pushed on Dec 24, 2023
  26. DrahtBot added the label CI failed on Jan 16, 2024
  27. ismaelsadeeq force-pushed on Jan 25, 2024
  28. DrahtBot removed the label CI failed on Jan 26, 2024
  29. ismaelsadeeq commented at 12:36 pm on January 30, 2024: member
    Rebased on master for green CI.
  30. achow101 requested review from luke-jr on Apr 9, 2024
  31. achow101 requested review from glozow on Apr 9, 2024
  32. ismaelsadeeq requested review from stickies-v on Apr 24, 2024
  33. DrahtBot added the label Needs rebase on May 15, 2024
  34. ismaelsadeeq force-pushed on May 17, 2024
  35. DrahtBot removed the label Needs rebase on May 17, 2024
  36. in src/validation.cpp:906 in 101a30833b outdated
    847@@ -848,8 +848,11 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
    848         return false; // state filled in by CheckTxInputs
    849     }
    850 
    851-    if (m_pool.m_opts.require_standard && !AreInputsStandard(tx, m_view)) {
    852-        return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
    853+    if (m_pool.m_opts.require_standard) {
    854+        state = HasNonStandardInput(tx, m_view);
    


    luke-jr commented at 5:32 pm on May 31, 2024:
    Seems like it’d be nicer to just pass state into the function rather than creating a new one and copying it.

    ismaelsadeeq commented at 6:42 pm on May 31, 2024:

    Seems like it’d be nicer to just pass state into the function

    I am taking this review comment as a non-blocking (nit) suggestion based on the tone.

    I will update this when there are blocking comments that indicate a need to retouch the diffs.

    Also, I would like to know whether this PR is conceptually acknowledged, @luke-jr. Thanks for your review.


    ismaelsadeeq commented at 9:45 pm on November 1, 2024:

    In this case, a new state is indeed created in HasNonStandardInput, but either RVO or an implicit move is performed not a copy?

    I think, in general, returning a value is preferred over using out parameters? same was also recommended here https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#coding-style-c-functions-and-methods

  37. DrahtBot added the label CI failed on Sep 11, 2024
  38. ismaelsadeeq force-pushed on Sep 14, 2024
  39. DrahtBot removed the label CI failed on Sep 14, 2024
  40. achow101 requested review from willcl-ark on Oct 15, 2024
  41. achow101 commented at 3:58 pm on October 15, 2024: member
    cc @tdb3
  42. in src/policy/policy.h:146 in 38a84db797 outdated
    146-*/
    147-bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
    148+ * Check for standard transaction types
    149+ * @param[in] mapInputs       Map of previous transactions that have outputs we're spending
    150+ * @returns valid TxValidationState if all inputs (scriptSigs) use only standard transaction forms else returns
    151+ * invalid TxValidationState which states why an input is not standard.
    


    tdb3 commented at 0:36 am on October 31, 2024:

    Could also clarify that invalid TxValidationState returned describes the first nonstandard input encountered.

    For example:

    0- * invalid TxValidationState which states why an input is not standard
    1+ * invalid TxValidationState which states why the first invalid input is not standard
    

    ismaelsadeeq commented at 8:07 pm on November 1, 2024:
    Done thanks
  43. in src/test/script_p2sh_tests.cpp:345 in 3ef7ac6f47 outdated
    341+    txFrom.vout[7].scriptPubKey = no_sigops;
    342+    txFrom.vout[7].nValue = 1000;
    343+
    344+    // vout [8] is non-standard because it contains OP_RETURN in its scriptSig.
    345+    static const unsigned char op_return[] = {OP_RETURN};
    346+    txFrom.vout[8].scriptPubKey = GetScriptForDestination(ScriptHash(CScript(op_return, op_return + sizeof(op_return))));
    


    tdb3 commented at 1:26 am on October 31, 2024:
    Instantiating the CScript here could allow re-use later (in txToNonStd4.vin[0].scriptSig = CScript(op_return, op_return + sizeof(op_return));)

    ismaelsadeeq commented at 8:08 pm on November 1, 2024:
    Good idea, done
  44. tdb3 commented at 1:33 am on October 31, 2024: contributor

    I’m a fan of the more descriptive errors.

    However, would some of these more descriptive errors flow down to RPC responses and cause issues for users? (e.g. testmempoolaccept).

    #30212 (comment)

    Does bitcoind consider this to be a breaking change? The new error message may be “more accurate”, but for clients that were matching on this error to figure out why a transaction was rejected, this breaks old behavior.

  45. policy: update `AreInputsStandard` to return error string
    This commit renames AreInputsStandard to HasNonStandardInput.
    
    HasNonStandardInput now returns valid TxValidationState if all inputs
    (scriptSigs) use only standard transaction forms else returns invalid
    TxValidationState which states why an input is not standard.
    095ac8adcd
  46. test: ensure `HasNonStandardInput` optionally returns debug string
    Test the various input scriptSig non standardness, and ensure HasNonStandardInput
    Returns the resin why the input is nonstandard.
    
    Co-authored-by: Ben Woosley <ben.woosley@gmail.com>
    a4a2b61784
  47. ismaelsadeeq force-pushed on Nov 1, 2024
  48. ismaelsadeeq force-pushed on Nov 1, 2024
  49. DrahtBot commented at 8:07 pm on November 1, 2024: contributor

    🚧 At least one of the CI tasks failed. Debug: https://github.com/bitcoin/bitcoin/runs/32405132725

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  50. DrahtBot added the label CI failed on Nov 1, 2024
  51. ismaelsadeeq commented at 8:08 pm on November 1, 2024: member

    Does bitcoind consider this to be a breaking change? The new error message may be “more accurate”, but for clients that were matching on this error to figure out why a transaction was rejected, this breaks old behavior.

    I’ve added a release notes for this

  52. doc: add release notes 02dd534672
  53. ismaelsadeeq force-pushed on Nov 1, 2024
  54. DrahtBot removed the label CI failed on Nov 1, 2024
  55. tdb3 commented at 6:26 pm on November 2, 2024: contributor

    Does bitcoind consider this to be a breaking change? The new error message may be “more accurate”, but for clients that were matching on this error to figure out why a transaction was rejected, this breaks old behavior.

    I’ve added a release notes for this

    The release note helps a lot. To err on the side of caution, it seems appropriate to include a -deprecatedrpc= option, to enable a period of deprecation for users.

  56. ismaelsadeeq commented at 8:49 pm on November 12, 2024: member

    The release note helps a lot. To err on the side of caution, it seems appropriate to include a -deprecatedrpc= option, to enable a period of deprecation for users.

    Thanks for your review @tdb3 will address this comment soon.

  57. ismaelsadeeq commented at 4:33 pm on November 15, 2024: member

    The release note helps a lot. To err on the side of caution, it seems appropriate to include a -deprecatedrpc= option, to enable a period of deprecation for users.

    I attempted this, but it’s a bit non trivial with some if-else branching. I also had to add a new TransactionError enum type to handle sendrawtransaction case.

      0diff --git a/src/common/messages.cpp b/src/common/messages.cpp
      1index 5fe3e9e4d86..3142ca07b4c 100644
      2--- a/src/common/messages.cpp
      3+++ b/src/common/messages.cpp
      4@@ -132,6 +132,8 @@ bilingual_str TransactionErrorString(const TransactionError err)
      5             return Untranslated("Transaction outputs already in utxo set");
      6         case TransactionError::MEMPOOL_REJECTED:
      7             return Untranslated("Transaction rejected by mempool");
      8+        case TransactionError::MEMPOOL_REJECTED_NON_STANDARD_INPUT:
      9+            return Untranslated("Transaction rejected by mempool");
     10         case TransactionError::MEMPOOL_ERROR:
     11             return Untranslated("Mempool internal error");
     12         case TransactionError::MAX_FEE_EXCEEDED:
     13diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
     14index 0f45da45dbd..c35bee4ee61 100644
     15--- a/src/node/transaction.cpp
     16+++ b/src/node/transaction.cpp
     17@@ -25,6 +25,9 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str
     18         if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
     19             return TransactionError::MISSING_INPUTS;
     20         }
     21+        if (state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD) {
     22+            return TransactionError::MEMPOOL_REJECTED_NON_STANDARD_INPUT;
     23+        }
     24         return TransactionError::MEMPOOL_REJECTED;
     25     } else {
     26         return TransactionError::MEMPOOL_ERROR;
     27diff --git a/src/node/types.h b/src/node/types.h
     28index 1302f1b127f..5cdb696204f 100644
     29--- a/src/node/types.h
     30+++ b/src/node/types.h
     31@@ -21,6 +21,7 @@ enum class TransactionError {
     32     MISSING_INPUTS,
     33     ALREADY_IN_UTXO_SET,
     34     MEMPOOL_REJECTED,
     35+    MEMPOOL_REJECTED_NON_STANDARD_INPUT,
     36     MEMPOOL_ERROR,
     37     MAX_FEE_EXCEEDED,
     38     MAX_BURN_EXCEEDED,
     39diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
     40index d61898260bf..a9f434fa605 100644
     41--- a/src/rpc/mempool.cpp
     42+++ b/src/rpc/mempool.cpp
     43@@ -95,6 +95,9 @@ static RPCHelpMan sendrawtransaction()
     44             NodeContext& node = EnsureAnyNodeContext(request.context);
     45             const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay=*/true, /*wait_callback=*/true);
     46             if (TransactionError::OK != err) {
     47+                if (IsDeprecatedRPCEnabled("nonstandard-inputs-err") && err == TransactionError::MEMPOOL_REJECTED_NON_STANDARD_INPUT) {
     48+                    throw JSONRPCTransactionError(err, "bad-txns-nonstandard-inputs");
     49+                }
     50                 throw JSONRPCTransactionError(err, err_string);
     51             }
     52 
     53@@ -241,7 +244,11 @@ static RPCHelpMan testmempoolaccept()
     54                     if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
     55                         result_inner.pushKV("reject-reason", "missing-inputs");
     56                     } else {
     57-                        result_inner.pushKV("reject-reason", state.GetRejectReason());
     58+                        if (IsDeprecatedRPCEnabled("nonstandard-inputs-err") && state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDA
     59RD) {
     60+                            result_inner.pushKV("reject-reason", "bad-txns-nonstandard-inputs");
     61+                        } else {
     62+                            result_inner.pushKV("reject-reason", state.GetRejectReason());
     63+                        }
     64                     }
     65                 }
     66                 rpc_result.push_back(std::move(result_inner));
     67@@ -961,7 +968,6 @@ static RPCHelpMan submitpackage()
     68             }
     69 
     70             UniValue rpc_result{UniValue::VOBJ};
     71-            rpc_result.pushKV("package_msg", package_msg);
     72             UniValue tx_result_map{UniValue::VOBJ};
     73             std::set<uint256> replaced_txids;
     74             for (const auto& tx : txns) {
     75@@ -979,7 +985,11 @@ static RPCHelpMan submitpackage()
     76                     result_inner.pushKV("other-wtxid", it->second.m_other_wtxid.value().GetHex());
     77                     break;
     78                 case MempoolAcceptResult::ResultType::INVALID:
     79-                    result_inner.pushKV("error", it->second.m_state.ToString());
     80+                    if (IsDeprecatedRPCEnabled("nonstandard-inputs-err") && it->second.m_state.GetResult() == TxValidationResult::TX_INPUTS_N
     81OT_STANDARD){
     82+                        result_inner.pushKV("error", "bad-txns-nonstandard-inputs");
     83+                    } else {
     84+                        result_inner.pushKV("error", it->second.m_state.ToString());
     85+                    }
     86                     break;
     87                 case MempoolAcceptResult::ResultType::VALID:
     88                 case MempoolAcceptResult::ResultType::MEMPOOL_ENTRY:
     89diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
     90index 678bac7a185..3653421dcae 100644
     91--- a/src/rpc/util.cpp
     92+++ b/src/rpc/util.cpp
     93@@ -400,6 +400,8 @@ RPCErrorCode RPCErrorFromTransactionError(TransactionError terr)
     94     switch (terr) {
     95         case TransactionError::MEMPOOL_REJECTED:
     96             return RPC_TRANSACTION_REJECTED;
     97+        case TransactionError::MEMPOOL_REJECTED_NON_STANDARD_INPUT:
     98+            return RPC_TRANSACTION_REJECTED;
     99         case TransactionError::ALREADY_IN_UTXO_SET:
    100             return RPC_VERIFY_ALREADY_IN_UTXO_SET;
    101         default: break;
    102diff --git a/src/test/fuzz/kitchen_sink.cpp b/src/test/fuzz/kitchen_sink.cpp
    103index 62b49106cdd..4df4a2c4372 100644
    104--- a/src/test/fuzz/kitchen_sink.cpp
    105+++ b/src/test/fuzz/kitchen_sink.cpp
    106@@ -25,6 +25,7 @@ constexpr TransactionError ALL_TRANSACTION_ERROR[] = {
    107     TransactionError::MISSING_INPUTS,
    108     TransactionError::ALREADY_IN_UTXO_SET,
    109     TransactionError::MEMPOOL_REJECTED,
    110+    TransactionError::MEMPOOL_REJECTED_NON_STANDARD_INPUT,
    111     TransactionError::MEMPOOL_ERROR,
    112     TransactionError::MAX_FEE_EXCEEDED,
    113 };
    

    However, do you think it would be better to make the prefix of the message bad-txns-nonstandard-inputs and append the additional information after it? This approach ensures that clients relying solely on pattern matching remain unaffected, as the error code remains unchanged.

    The only clients that affected after the above suggestion are those performing an exact match.

    Let me know what you think about the two approaches.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-12-03 15:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me