diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -114,6 +114,7 @@ test/sigencoding_tests.cpp \ test/sighash_tests.cpp \ test/sighashtype_tests.cpp \ + test/sigcheckcount_tests.cpp \ test/sigopcount_tests.cpp \ test/sigutil.h \ test/skiplist_tests.cpp \ diff --git a/src/script/interpreter.h b/src/script/interpreter.h --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -82,9 +82,24 @@ using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker; +/** + * Struct for holding cumulative results from executing a script or a sequence + * of scripts. + */ +struct ScriptExecutionMetrics { + int nSigChecks = 0; +}; + bool EvalScript(std::vector> &stack, const CScript &script, uint32_t flags, const BaseSignatureChecker &checker, - ScriptError *error = nullptr); + ScriptExecutionMetrics &metrics, ScriptError *error = nullptr); +static inline bool EvalScript(std::vector> &stack, + const CScript &script, uint32_t flags, + const BaseSignatureChecker &checker, + ScriptError *error = nullptr) { + ScriptExecutionMetrics dummymetrics; + return EvalScript(stack, script, flags, checker, dummymetrics, error); +} bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey, uint32_t flags, const BaseSignatureChecker &checker, ScriptError *serror = nullptr); diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -102,7 +102,7 @@ bool EvalScript(std::vector &stack, const CScript &script, uint32_t flags, const BaseSignatureChecker &checker, - ScriptError *serror) { + ScriptExecutionMetrics &metrics, ScriptError *serror) { static const CScriptNum bnZero(0); static const CScriptNum bnOne(1); static const valtype vchFalse(0); @@ -884,6 +884,10 @@ bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, flags); + if (vchSig.size()) { + metrics.nSigChecks += 1; + } + if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size()) { return set_error(serror, ScriptError::SIG_NULLFAIL); @@ -929,6 +933,8 @@ .Finalize(vchHash.data()); fSuccess = checker.VerifySignature( vchSig, CPubKey(vchPubKey), uint256(vchHash)); + + metrics.nSigChecks += 1; } if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && @@ -1083,6 +1089,10 @@ return set_error(serror, ScriptError::SIG_NULLFAIL); } + + // this is guaranteed to execute exactly + // nSigsCount times (if not script error) + metrics.nSigChecks += 1; } if ((checkBits >> iKey) != 0) { @@ -1154,6 +1164,14 @@ return set_error(serror, ScriptError::SIG_NULLFAIL); } + + if (!areAllSignaturesNull) { + // This is not identical to the number of actual + // ECDSA verifies, but, it is an upper bound + // that can be easily determined without doing + // CPU-intensive checks. + metrics.nSigChecks += nKeysCount; + } } // Clean up stack of all arguments diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -127,6 +127,7 @@ sigencoding_tests.cpp sighash_tests.cpp sighashtype_tests.cpp + sigcheckcount_tests.cpp sigopcount_tests.cpp sigutil.cpp skiplist_tests.cpp diff --git a/src/test/sigcheckcount_tests.cpp b/src/test/sigcheckcount_tests.cpp new file mode 100644 --- /dev/null +++ b/src/test/sigcheckcount_tests.cpp @@ -0,0 +1,294 @@ +// Copyright (c) 2019 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include