diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -92,6 +92,7 @@ test/scriptflags.h \ test/scriptnum_tests.cpp \ test/serialize_tests.cpp \ + test/sigcache_tests.cpp \ test/sigencoding_tests.cpp \ test/sighash_tests.cpp \ test/sighashtype_tests.cpp \ diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1472,7 +1472,11 @@ * flags, either explicit or implicit, they should be noted in sigcache.cpp * by making sure said flags are omitted from INVARIANT_FLAGS. */ - return pubkey.VerifyECDSA(sighash, vchSig); + if (IsSchnorrSignature(vchSig, flags)) { + return pubkey.VerifySchnorr(sighash, vchSig); + } else { + return pubkey.VerifyECDSA(sighash, vchSig); + } } bool TransactionSignatureChecker::CheckSig( diff --git a/src/script/script_flags.h b/src/script/script_flags.h --- a/src/script/script_flags.h +++ b/src/script/script_flags.h @@ -98,6 +98,13 @@ // Is OP_CHECKDATASIG and variant are enabled. // SCRIPT_ENABLE_CHECKDATASIG = (1U << 18), + + // When enabled, 64-byte (+hashtype) signatures will be interpreted as + // Schnorr signatures instead of as ECDSA. Some opcodes may override this + // flag. + // + SCRIPT_ENABLE_SCHNORR = (1U << 19), + }; #endif // BITCOIN_SCRIPT_SCRIPTFLAGS_H diff --git a/src/script/sigencoding.h b/src/script/sigencoding.h --- a/src/script/sigencoding.h +++ b/src/script/sigencoding.h @@ -8,6 +8,7 @@ #define BITCOIN_SCRIPT_SIGENCODING_H #include "script_error.h" +#include "script_flags.h" #include "sighashtype.h" #include @@ -27,6 +28,14 @@ } // namespace +/* + * Test whether a given signature (without its hashtype byte) should be + * classified as a Schnorr signature. + */ +inline bool IsSchnorrSignature(const valtype &vchSig, uint32_t flags) { + return (flags & SCRIPT_ENABLE_SCHNORR) && (vchSig.size() == 64); +} + /** * Check that the signature provided on some data is properly encoded. * Signatures passed to OP_CHECKDATASIG and its verify variant must be checked diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -108,6 +108,7 @@ scriptflags.cpp scriptnum_tests.cpp serialize_tests.cpp + sigcache_tests.cpp sigencoding_tests.cpp sighash_tests.cpp sighashtype_tests.cpp diff --git a/src/test/scriptflags.cpp b/src/test/scriptflags.cpp --- a/src/test/scriptflags.cpp +++ b/src/test/scriptflags.cpp @@ -4,7 +4,7 @@ #include "test/scriptflags.h" -#include "script/interpreter.h" +#include "script/script_flags.h" #include #include @@ -32,6 +32,7 @@ {"SIGHASH_FORKID", SCRIPT_ENABLE_SIGHASH_FORKID}, {"REPLAY_PROTECTION", SCRIPT_ENABLE_REPLAY_PROTECTION}, {"CHECKDATASIG", SCRIPT_ENABLE_CHECKDATASIG}, + {"SCHNORR", SCRIPT_ENABLE_SCHNORR}, }; uint32_t ParseScriptFlags(std::string strFlags) { diff --git a/src/test/sigcache_tests.cpp b/src/test/sigcache_tests.cpp new file mode 100644 --- /dev/null +++ b/src/test/sigcache_tests.cpp @@ -0,0 +1,85 @@ +// Copyright (c) 2012-2015 The Bitcoin Core developers +// 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. +// (based on key_tests.cpp) + +#include "key.h" + +#include "base58.h" +#include "dstencode.h" +#include "script/interpreter.h" +#include "script/script.h" +#include "script/sigcache.h" +#include "test/test_bitcoin.h" +#include "uint256.h" +#include "util.h" +#include "utilstrencodings.h" + +#include +#include + +#include + +static const std::string strSecret1 = + "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; + +BOOST_FIXTURE_TEST_SUITE(sigcache_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(invariant_tests) { + // In this test, we are going to test whether VerifySignature correctly + // varies the hash when we tweak sensitive flags (namely, + // SCRIPT_ENABLE_SCHNORR) + + // Create a dummy transaction since the sigcache is only accessible + // via CachingTransactionSignatureChecker, which requires a tx. + CDataStream stream( + ParseHex( + "010000000122739e70fbee987a8be1788395a2f2e6ad18ccb7ff611cd798071539" + "dde3c38e000000000151ffffffff010000000000000000016a00000000"), + SER_NETWORK, PROTOCOL_VERSION); + CTransaction dummyTx(deserialize, stream); + PrecomputedTransactionData txdata(dummyTx); + + // We access sigcache through CachingTransactionSignatureChecker + CachingTransactionSignatureChecker checker(&dummyTx, 0, 0 * SATOSHI, true, + txdata); + + // use these flags as a backround basis for the test + uint32_t flags = SCRIPT_VERIFY_NONE; + + CBitcoinSecret bsecret1; + BOOST_CHECK(bsecret1.SetString(strSecret1)); + + CKey key1 = bsecret1.GetKey(); + BOOST_CHECK(key1.IsCompressed() == false); + + CPubKey pubkey1 = key1.GetPubKey(); + + BOOST_CHECK(key1.VerifyPubKey(pubkey1)); + + for (int n = 0; n < 16; n++) { + std::string strMsg = strprintf("Sigcache test %i: 11", n); + uint256 hashMsg = Hash(strMsg.begin(), strMsg.end()); + + // Make Schnorr signature (64 bytes) + std::vector sigSchnorr; + BOOST_CHECK(key1.SignSchnorr(hashMsg, sigSchnorr)); + + // direct testing + BOOST_CHECK(pubkey1.VerifySchnorr(hashMsg, sigSchnorr)); + BOOST_CHECK(!pubkey1.VerifyECDSA(hashMsg, sigSchnorr)); + + // put valid Schnorr signature into cache + uint32_t schnorr_flags = flags | SCRIPT_ENABLE_SCHNORR; + BOOST_CHECK(checker.VerifySignature(sigSchnorr, pubkey1, hashMsg, + schnorr_flags)); + // now try verifying it with Schnorrs disabled -- it should + // miss the cache and thereafter fail to verify as ECDSA + uint32_t ecdsa_flags = flags & ~SCRIPT_ENABLE_SCHNORR; + BOOST_CHECK(!checker.VerifySignature(sigSchnorr, pubkey1, hashMsg, + ecdsa_flags)); + } +} + +BOOST_AUTO_TEST_SUITE_END()