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/sigcache.h b/src/script/sigcache.h --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -43,6 +43,9 @@ private: bool store; + bool IsCached(const std::vector &vchSig, const CPubKey &vchPubKey, + const uint256 &sighash) const; + public: CachingTransactionSignatureChecker(const CTransaction *txToIn, unsigned int nInIn, @@ -54,6 +57,8 @@ bool VerifySignature(const std::vector &vchSig, const CPubKey &vchPubKey, const uint256 &sighash) const override; + + friend class TestCachingTransactionSignatureChecker; }; void InitSignatureCache(); diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -80,6 +80,14 @@ (nElems * sizeof(uint256)) >> 20, nMaxCacheSize >> 20, nElems); } +bool CachingTransactionSignatureChecker::IsCached( + const std::vector &vchSig, const CPubKey &pubkey, + const uint256 &sighash) const { + uint256 entry; + signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey); + return signatureCache.Get(entry, false); +} + bool CachingTransactionSignatureChecker::VerifySignature( const std::vector &vchSig, const CPubKey &pubkey, const uint256 &sighash) const { 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/sigcache_tests.cpp b/src/test/sigcache_tests.cpp new file mode 100644 --- /dev/null +++ b/src/test/sigcache_tests.cpp @@ -0,0 +1,112 @@ +// 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 "script/sigcache.h" + +#include "base58.h" +#include "dstencode.h" +#include "key.h" +#include "test/test_bitcoin.h" +#include "utilstrencodings.h" + +#include + +#include +#include + +static const std::string strSecret1 = + "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; +static const std::string strSecret1C = + "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; + +class TestCachingTransactionSignatureChecker { + /** + * Sigcache is only accessible via CachingTransactionSignatureChecker, which + * requires a tx. So we make a dummy transaction to populate it. + */ + CDataStream stream; + CTransaction dummyTx; + PrecomputedTransactionData txdata; + CachingTransactionSignatureChecker checker; + +public: + TestCachingTransactionSignatureChecker() + : stream(ParseHex("010000000122739e70fbee987a8be1788395a2f2e6ad18ccb7ff" + "611cd798071539dde3c38e000000000151ffffffff0100000000" + "00000000016a00000000"), + SER_NETWORK, PROTOCOL_VERSION), + dummyTx(deserialize, stream), txdata(dummyTx), + checker(&dummyTx, 0, 0 * SATOSHI, true, txdata) {} + + inline bool VerifyAndStore(const std::vector &vchSig, + const CPubKey &pubkey, const uint256 &sighash) { + return checker.VerifySignature(vchSig, pubkey, sighash); + } + + inline bool IsCached(const std::vector &vchSig, + const CPubKey &pubkey, const uint256 &sighash) { + return checker.IsCached(vchSig, pubkey, sighash); + } +}; + +BOOST_FIXTURE_TEST_SUITE(sigcache_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(test1) { + TestCachingTransactionSignatureChecker checker; + + CBitcoinSecret bsecret1, bsecret1C; + BOOST_CHECK(bsecret1.SetString(strSecret1)); + BOOST_CHECK(bsecret1C.SetString(strSecret1C)); + + CKey key1 = bsecret1.GetKey(); + BOOST_CHECK(key1.IsCompressed() == false); + CKey key1C = bsecret1C.GetKey(); + BOOST_CHECK(key1C.IsCompressed() == true); + + CPubKey pubkey1 = key1.GetPubKey(); + CPubKey pubkey1C = key1C.GetPubKey(); + + for (int n = 0; n < 16; n++) { + std::string strMsg = strprintf("Sigcache test1 %i: xx", n); + uint256 hashMsg = Hash(strMsg.begin(), strMsg.end()); + uint256 hashMsg2 = Hash(strMsg.begin() + 1, strMsg.end()); + + std::vector sig; + BOOST_CHECK(key1.SignECDSA(hashMsg, sig)); + std::vector sig2; + BOOST_CHECK(key1.SignECDSA(hashMsg2, sig2)); + + // cross-check + BOOST_CHECK(!checker.VerifyAndStore(sig2, pubkey1, hashMsg)); + BOOST_CHECK(!checker.VerifyAndStore(sig, pubkey1, hashMsg2)); + // that should not have put them in cache... + BOOST_CHECK(!checker.IsCached(sig2, pubkey1, hashMsg)); + BOOST_CHECK(!checker.IsCached(sig, pubkey1, hashMsg2)); + + // check that it's not in cache at start + BOOST_CHECK(!checker.IsCached(sig, pubkey1, hashMsg)); + BOOST_CHECK(!checker.IsCached(sig2, pubkey1, hashMsg2)); + // Insert into cache + BOOST_CHECK(checker.VerifyAndStore(sig, pubkey1, hashMsg)); + BOOST_CHECK(checker.VerifyAndStore(sig2, pubkey1, hashMsg2)); + // check that it's in + BOOST_CHECK(checker.IsCached(sig, pubkey1, hashMsg)); + BOOST_CHECK(checker.IsCached(sig2, pubkey1, hashMsg2)); + // check that different signature hits different entry + BOOST_CHECK(!checker.IsCached(sig2, pubkey1, hashMsg)); + // check that compressed pubkey hits different entry + BOOST_CHECK(!checker.IsCached(sig, pubkey1C, hashMsg)); + // check that different message hits different entry + BOOST_CHECK(!checker.IsCached(sig, pubkey1, hashMsg2)); + + // compressed key is for same privkey, so verifying works: + BOOST_CHECK(checker.VerifyAndStore(sig, pubkey1C, hashMsg)); + // now we *should* get a hit + BOOST_CHECK(checker.IsCached(sig, pubkey1C, hashMsg)); + } +} + +BOOST_AUTO_TEST_SUITE_END()