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); @@ -885,6 +885,7 @@ fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, flags); + metrics.nSigChecks += 1; if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL)) { return set_error(serror, @@ -932,6 +933,7 @@ .Finalize(vchHash.data()); fSuccess = checker.VerifySignature( vchSig, CPubKey(vchPubKey), uint256(vchHash)); + metrics.nSigChecks += 1; if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL)) { return set_error(serror, @@ -1086,6 +1088,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) { @@ -1157,6 +1163,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 @@ -135,6 +135,7 @@ sigencoding_tests.cpp sighash_tests.cpp sighashtype_tests.cpp + sigcheckcount_tests.cpp sigopcount_tests.cpp skiplist_tests.cpp streams_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,308 @@ +// 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