From 7b267c034fdc2c8cb964a763f182ff98a75ba2ac Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 18 Oct 2024 06:53:44 -0400 Subject: [PATCH] [validation] Add detailed txin/txout information for script error messages Don't just report which script error occurred, but which in which input of which transaction, and which UTXO was being spent. --- src/validation.cpp | 15 ++++++++------- src/validation.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 54901b2c58..b8f89f4b88 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2103,14 +2103,15 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund AddCoins(inputs, tx, nHeight); } -std::optional CScriptCheck::operator()() { +std::optional> CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness; ScriptError error{SCRIPT_ERR_UNKNOWN_ERROR}; if (VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error)) { return std::nullopt; } else { - return error; + auto debug_str = strprintf("input %i of %s (wtxid %s), spending %s:%i", nIn, ptxTo->GetHash().ToString(), ptxTo->GetWitnessHash().ToString(), ptxTo->vin[nIn].prevout.hash.ToString(), ptxTo->vin[nIn].prevout.n); + return std::make_pair(error, std::move(debug_str)); } } @@ -2214,7 +2215,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state, 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))); + 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 @@ -2228,7 +2229,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state, // MANDATORY flag failures correspond to // TxValidationResult::TX_CONSENSUS. - return state.Invalid(TxValidationResult::TX_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(*result))); + return state.Invalid(TxValidationResult::TX_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(result->first)), result->second); } } @@ -2688,7 +2689,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, // Any transaction validation failure in ConnectBlock is a block consensus failure state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(), tx_state.GetDebugMessage()); - LogInfo("Script validation error in block: %s\n", tx_state.GetRejectReason()); + LogInfo("Script validation error in block: %s\n", state.ToString()); return false; } control.Add(std::move(vChecks)); @@ -2716,8 +2717,8 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, auto parallel_result = control.Complete(); if (parallel_result.has_value()) { - state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(*parallel_result))); - LogInfo("Script validation error in block: %s", state.GetRejectReason()); + state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(parallel_result->first)), parallel_result->second); + LogInfo("Script validation error in block: %s", state.ToString()); return false; } const auto time_4{SteadyClock::now()}; diff --git a/src/validation.h b/src/validation.h index 14fe145730..6b67dc485b 100644 --- a/src/validation.h +++ b/src/validation.h @@ -347,7 +347,7 @@ public: CScriptCheck(CScriptCheck&&) = default; CScriptCheck& operator=(CScriptCheck&&) = default; - std::optional operator()(); + std::optional> operator()(); }; // CScriptCheck is used a lot in std::vector, make sure that's efficient