diff --git a/src/script/interpreter.h b/src/script/interpreter.h --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -100,9 +100,25 @@ ScriptExecutionMetrics dummymetrics; return EvalScript(stack, script, flags, checker, dummymetrics, error); } + +/** + * Execute an unlocking and locking script together. + * + * Upon success, metrics will hold the accumulated script metrics. + * (upon failure, the results should not be relied on) + */ bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey, uint32_t flags, const BaseSignatureChecker &checker, + ScriptExecutionMetrics &metricsOut, ScriptError *serror = nullptr); +static inline bool VerifyScript(const CScript &scriptSig, + const CScript &scriptPubKey, uint32_t flags, + const BaseSignatureChecker &checker, + ScriptError *serror = nullptr) { + ScriptExecutionMetrics dummymetrics; + return VerifyScript(scriptSig, scriptPubKey, flags, checker, dummymetrics, + serror); +} int FindAndDelete(CScript &script, const CScript &b); diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1692,7 +1692,7 @@ bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey, uint32_t flags, const BaseSignatureChecker &checker, - ScriptError *serror) { + ScriptExecutionMetrics &metricsOut, ScriptError *serror) { set_error(serror, ScriptError::UNKNOWN); // If FORKID is enabled, we also ensure strict encoding. @@ -1704,15 +1704,17 @@ return set_error(serror, ScriptError::SIG_PUSHONLY); } + ScriptExecutionMetrics metrics = {}; + std::vector stack, stackCopy; - if (!EvalScript(stack, scriptSig, flags, checker, serror)) { + if (!EvalScript(stack, scriptSig, flags, checker, metrics, serror)) { // serror is set return false; } if (flags & SCRIPT_VERIFY_P2SH) { stackCopy = stack; } - if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) { + if (!EvalScript(stack, scriptPubKey, flags, checker, metrics, serror)) { // serror is set return false; } @@ -1747,10 +1749,12 @@ // pushed onto the stack. if ((flags & SCRIPT_DISALLOW_SEGWIT_RECOVERY) == 0 && stack.empty() && pubKey2.IsWitnessProgram()) { + // must set metricsOut for all successful returns + metricsOut = metrics; return set_success(serror); } - if (!EvalScript(stack, pubKey2, flags, checker, serror)) { + if (!EvalScript(stack, pubKey2, flags, checker, metrics, serror)) { // serror is set return false; } @@ -1776,5 +1780,6 @@ } } + metricsOut = metrics; return set_success(serror); } diff --git a/src/test/sigcheckcount_tests.cpp b/src/test/sigcheckcount_tests.cpp --- a/src/test/sigcheckcount_tests.cpp +++ b/src/test/sigcheckcount_tests.cpp @@ -308,4 +308,41 @@ } } +BOOST_AUTO_TEST_CASE(test_verifyscript) { + // make sure that verifyscript is correctly resetting and accumulating + // sigchecks for the input. + + ScriptExecutionMetrics metrics; + + // regardless of the initial value, it gets zeroed out + metrics.nSigChecks = 12345; + BOOST_CHECK(VerifyScript(CScript() << OP_1, CScript(), 0, dummysigchecker, + metrics)); + BOOST_CHECK_EQUAL(metrics.nSigChecks, 0); + + // it even gets zeroed out for segwit recovery special case (which returns + // true from an alternative location) + metrics.nSigChecks = 12345; + CScript swscript; + swscript << OP_0 << std::vector(20); + BOOST_CHECK(VerifyScript(CScript() << ToByteVector(swscript), + CScript() << OP_HASH160 + << ToByteVector(CScriptID(swscript)) + << OP_EQUAL, + SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_CLEANSTACK, + dummysigchecker, metrics)); + BOOST_CHECK_EQUAL(metrics.nSigChecks, 0); + + // If signature checks somehow occur in scriptsig, they do get counted. + // This can happen in historical blocks pre SIGPUSHONLY, even with CHECKSIG. + // (an analogous check for P2SH is not possible since it enforces + // sigpushonly). + BOOST_CHECK(VerifyScript(CScript() << sigschnorr << msg << pub + << OP_CHECKDATASIG /* scriptSig */, + CScript() << sigecdsa << msg << pub + << OP_CHECKDATASIG /* scriptPubKey */, + 0, dummysigchecker, metrics)); + BOOST_CHECK_EQUAL(metrics.nSigChecks, 2); +} + BOOST_AUTO_TEST_SUITE_END()