Changeset View
Changeset View
Standalone View
Standalone View
src/test/sigcache_tests.cpp
Show All 16 Lines | |||||
#include <string> | #include <string> | ||||
#include <vector> | #include <vector> | ||||
static const std::string strSecret1 = | static const std::string strSecret1 = | ||||
"5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; | "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; | ||||
static const std::string strSecret1C = | static const std::string strSecret1C = | ||||
"Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; | "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; | ||||
class TestCachingTransactionSignatureChecker { | /* We will be testing that these flags do not affect the cache entry. | ||||
* This list must match the one found in script/sigcache.cpp , however | |||||
* we duplicate it here to make sure that changes in cache behaviour also | |||||
* require an intentional change to this test. | |||||
*/ | |||||
static const uint32_t INVARIANT_FLAGS = | |||||
deadalnix: Pick a different name in the test and the code. | |||||
markblundebergAuthorUnsubmitted Done Inline Actionsok markblundeberg: ok | |||||
SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_DERSIG | | |||||
SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_NULLDUMMY | SCRIPT_VERIFY_SIGPUSHONLY | | |||||
SCRIPT_VERIFY_MINIMALDATA | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS | | |||||
SCRIPT_VERIFY_CLEANSTACK | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | | |||||
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY | SCRIPT_VERIFY_MINIMALIF | | |||||
SCRIPT_VERIFY_NULLFAIL | SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE | | |||||
SCRIPT_ENABLE_SIGHASH_FORKID | SCRIPT_ENABLE_REPLAY_PROTECTION | | |||||
SCRIPT_ENABLE_CHECKDATASIG; | |||||
/* We will be testing that these flags DO affect the cache entry. The expected | |||||
* behaviour is that flags which are not explicitly listed as invariant in | |||||
* script/sigcache.cpp will affect the cache entry. | |||||
*/ | |||||
static const uint32_t VARIANT_FLAGS = ~INVARIANT_FLAGS; | |||||
deadalnixUnsubmitted Done Inline ActionsThis should probably be an explicit list. deadalnix: This should probably be an explicit list. | |||||
markblundebergAuthorUnsubmitted Done Inline ActionsI was thinking so, but, unfortunately it would look a bit ugly since it tests all the unnamed flags - (1U << 11) & (1U << 12) & (1U <<19) & (1U << 20) & ... & (1U << 31). When I add SCRIPT_ENABLE_SCHNORR it will be listed explicitly, regardless. markblundeberg: I was thinking so, but, unfortunately it would look a bit ugly since it tests all the unnamed… | |||||
deadalnixUnsubmitted Done Inline ActionsUnnamed flags do not have a set behavior yet, so there is really not much of a point testing them. deadalnix: Unnamed flags do not have a set behavior yet, so there is really not much of a point testing… | |||||
/** | /** | ||||
* Sigcache is only accessible via CachingTransactionSignatureChecker | * Sigcache is only accessible via CachingTransactionSignatureChecker | ||||
* as friend. | |||||
*/ | */ | ||||
class TestCachingTransactionSignatureChecker { | |||||
CachingTransactionSignatureChecker *pchecker; | CachingTransactionSignatureChecker *pchecker; | ||||
public: | public: | ||||
TestCachingTransactionSignatureChecker( | TestCachingTransactionSignatureChecker( | ||||
CachingTransactionSignatureChecker &checkerarg) { | CachingTransactionSignatureChecker &checkerarg) { | ||||
pchecker = &checkerarg; | pchecker = &checkerarg; | ||||
} | } | ||||
inline bool VerifyAndStore(const std::vector<uint8_t> &vchSig, | inline bool VerifyAndStore(const std::vector<uint8_t> &vchSig, | ||||
const CPubKey &pubkey, const uint256 &sighash) { | const CPubKey &pubkey, const uint256 &sighash, | ||||
return pchecker->VerifySignature(vchSig, pubkey, sighash); | uint32_t flags) { | ||||
return pchecker->VerifySignature(vchSig, pubkey, sighash, flags); | |||||
} | } | ||||
inline bool IsCached(const std::vector<uint8_t> &vchSig, | inline bool IsCached(const std::vector<uint8_t> &vchSig, | ||||
const CPubKey &pubkey, const uint256 &sighash) { | const CPubKey &pubkey, const uint256 &sighash, | ||||
return pchecker->IsCached(vchSig, pubkey, sighash); | uint32_t flags) { | ||||
return pchecker->IsCached(vchSig, pubkey, sighash, flags); | |||||
} | } | ||||
}; | }; | ||||
BOOST_FIXTURE_TEST_SUITE(sigcache_tests, BasicTestingSetup) | BOOST_FIXTURE_TEST_SUITE(sigcache_tests, BasicTestingSetup) | ||||
BOOST_AUTO_TEST_CASE(test1) { | BOOST_AUTO_TEST_CASE(sig_pubkey_hash_variations) { | ||||
/** | /** | ||||
* Making CachingTransactionSignatureChecke requires a tx. So we make a | * Making CachingTransactionSignatureChecker requires a tx. So we make a | ||||
* dummy transaction (doesn't matter what it is) to construct it. | * dummy transaction (doesn't matter what it is) to construct it. | ||||
*/ | */ | ||||
CDataStream stream( | CDataStream stream( | ||||
ParseHex( | ParseHex( | ||||
"010000000122739e70fbee987a8be1788395a2f2e6ad18ccb7ff611cd798071539" | "010000000122739e70fbee987a8be1788395a2f2e6ad18ccb7ff611cd798071539" | ||||
"dde3c38e000000000151ffffffff010000000000000000016a00000000"), | "dde3c38e000000000151ffffffff010000000000000000016a00000000"), | ||||
SER_NETWORK, PROTOCOL_VERSION); | SER_NETWORK, PROTOCOL_VERSION); | ||||
CTransaction dummyTx(deserialize, stream); | CTransaction dummyTx(deserialize, stream); | ||||
PrecomputedTransactionData txdata(dummyTx); | PrecomputedTransactionData txdata(dummyTx); | ||||
CachingTransactionSignatureChecker checker(&dummyTx, 0, 0 * SATOSHI, true, | CachingTransactionSignatureChecker checker(&dummyTx, 0, 0 * SATOSHI, true, | ||||
txdata); | txdata); | ||||
TestCachingTransactionSignatureChecker testChecker(checker); | TestCachingTransactionSignatureChecker testChecker(checker); | ||||
uint32_t flags = 0; | |||||
CBitcoinSecret bsecret1, bsecret1C; | CBitcoinSecret bsecret1, bsecret1C; | ||||
BOOST_CHECK(bsecret1.SetString(strSecret1)); | BOOST_CHECK(bsecret1.SetString(strSecret1)); | ||||
BOOST_CHECK(bsecret1C.SetString(strSecret1C)); | BOOST_CHECK(bsecret1C.SetString(strSecret1C)); | ||||
CKey key1 = bsecret1.GetKey(); | CKey key1 = bsecret1.GetKey(); | ||||
BOOST_CHECK(key1.IsCompressed() == false); | BOOST_CHECK(key1.IsCompressed() == false); | ||||
CKey key1C = bsecret1C.GetKey(); | CKey key1C = bsecret1C.GetKey(); | ||||
BOOST_CHECK(key1C.IsCompressed() == true); | BOOST_CHECK(key1C.IsCompressed() == true); | ||||
CPubKey pubkey1 = key1.GetPubKey(); | CPubKey pubkey1 = key1.GetPubKey(); | ||||
CPubKey pubkey1C = key1C.GetPubKey(); | CPubKey pubkey1C = key1C.GetPubKey(); | ||||
for (int n = 0; n < 16; n++) { | for (int n = 0; n < 16; n++) { | ||||
std::string strMsg = strprintf("Sigcache test1 %i: xx", n); | std::string strMsg = strprintf("Sigcache test1 %i: xx", n); | ||||
uint256 hashMsg = Hash(strMsg.begin(), strMsg.end()); | uint256 hashMsg = Hash(strMsg.begin(), strMsg.end()); | ||||
uint256 hashMsg2 = Hash(strMsg.begin() + 1, strMsg.end()); | uint256 hashMsg2 = Hash(strMsg.begin() + 1, strMsg.end()); | ||||
std::vector<uint8_t> sig; | std::vector<uint8_t> sig; | ||||
BOOST_CHECK(key1.SignECDSA(hashMsg, sig)); | BOOST_CHECK(key1.SignECDSA(hashMsg, sig)); | ||||
std::vector<uint8_t> sig2; | std::vector<uint8_t> sig2; | ||||
BOOST_CHECK(key1.SignECDSA(hashMsg2, sig2)); | BOOST_CHECK(key1.SignECDSA(hashMsg2, sig2)); | ||||
// cross-check | // cross-check | ||||
BOOST_CHECK(!testChecker.VerifyAndStore(sig2, pubkey1, hashMsg)); | BOOST_CHECK(!testChecker.VerifyAndStore(sig2, pubkey1, hashMsg, flags)); | ||||
BOOST_CHECK(!testChecker.VerifyAndStore(sig, pubkey1, hashMsg2)); | BOOST_CHECK(!testChecker.VerifyAndStore(sig, pubkey1, hashMsg2, flags)); | ||||
// that should not have put them in cache... | // that should not have put them in cache... | ||||
BOOST_CHECK(!testChecker.IsCached(sig2, pubkey1, hashMsg)); | BOOST_CHECK(!testChecker.IsCached(sig2, pubkey1, hashMsg, flags)); | ||||
BOOST_CHECK(!testChecker.IsCached(sig, pubkey1, hashMsg2)); | BOOST_CHECK(!testChecker.IsCached(sig, pubkey1, hashMsg2, flags)); | ||||
// check that it's not in cache at start | // check that it's not in cache at start | ||||
BOOST_CHECK(!testChecker.IsCached(sig, pubkey1, hashMsg)); | BOOST_CHECK(!testChecker.IsCached(sig, pubkey1, hashMsg, flags)); | ||||
BOOST_CHECK(!testChecker.IsCached(sig2, pubkey1, hashMsg2)); | BOOST_CHECK(!testChecker.IsCached(sig2, pubkey1, hashMsg2, flags)); | ||||
// Insert into cache | // Insert into cache | ||||
BOOST_CHECK(testChecker.VerifyAndStore(sig, pubkey1, hashMsg)); | BOOST_CHECK(testChecker.VerifyAndStore(sig, pubkey1, hashMsg, flags)); | ||||
BOOST_CHECK(testChecker.VerifyAndStore(sig2, pubkey1, hashMsg2)); | BOOST_CHECK(testChecker.VerifyAndStore(sig2, pubkey1, hashMsg2, flags)); | ||||
// check that it's in | // check that it's in | ||||
BOOST_CHECK(testChecker.IsCached(sig, pubkey1, hashMsg)); | BOOST_CHECK(testChecker.IsCached(sig, pubkey1, hashMsg, flags)); | ||||
BOOST_CHECK(testChecker.IsCached(sig2, pubkey1, hashMsg2)); | BOOST_CHECK(testChecker.IsCached(sig2, pubkey1, hashMsg2, flags)); | ||||
// check that different signature hits different entry | // check that different signature hits different entry | ||||
BOOST_CHECK(!testChecker.IsCached(sig2, pubkey1, hashMsg)); | BOOST_CHECK(!testChecker.IsCached(sig2, pubkey1, hashMsg, flags)); | ||||
// check that compressed pubkey hits different entry | // check that compressed pubkey hits different entry | ||||
BOOST_CHECK(!testChecker.IsCached(sig, pubkey1C, hashMsg)); | BOOST_CHECK(!testChecker.IsCached(sig, pubkey1C, hashMsg, flags)); | ||||
// check that different message hits different entry | // check that different message hits different entry | ||||
BOOST_CHECK(!testChecker.IsCached(sig, pubkey1, hashMsg2)); | BOOST_CHECK(!testChecker.IsCached(sig, pubkey1, hashMsg2, flags)); | ||||
// compressed key is for same privkey, so verifying works: | // compressed key is for same privkey, so verifying works: | ||||
BOOST_CHECK(testChecker.VerifyAndStore(sig, pubkey1C, hashMsg)); | BOOST_CHECK(testChecker.VerifyAndStore(sig, pubkey1C, hashMsg, flags)); | ||||
// now we *should* get a hit | // now we *should* get a hit | ||||
BOOST_CHECK(testChecker.IsCached(sig, pubkey1C, hashMsg)); | BOOST_CHECK(testChecker.IsCached(sig, pubkey1C, hashMsg, flags)); | ||||
} | |||||
} | |||||
BOOST_AUTO_TEST_CASE(flag_invariants) { | |||||
/** | |||||
* Making CachingTransactionSignatureChecker requires a tx. So we make a | |||||
* dummy transaction (doesn't matter what it is) to construct it. | |||||
*/ | |||||
CDataStream stream( | |||||
ParseHex( | |||||
"010000000122739e70fbee987a8be1788395a2f2e6ad18ccb7ff611cd798071539" | |||||
"dde3c38e000000000151ffffffff010000000000000000016a00000000"), | |||||
SER_NETWORK, PROTOCOL_VERSION); | |||||
CTransaction dummyTx(deserialize, stream); | |||||
PrecomputedTransactionData txdata(dummyTx); | |||||
CachingTransactionSignatureChecker checker(&dummyTx, 0, 0 * SATOSHI, true, | |||||
txdata); | |||||
TestCachingTransactionSignatureChecker testChecker(checker); | |||||
CBitcoinSecret bsecret1; | |||||
bsecret1.SetString(strSecret1); | |||||
CKey key1 = bsecret1.GetKey(); | |||||
CPubKey pubkey1 = key1.GetPubKey(); | |||||
// there should not be any overlap | |||||
BOOST_REQUIRE((VARIANT_FLAGS & INVARIANT_FLAGS) == 0); | |||||
for (int n = 0; n < 16; n++) { | |||||
std::string strMsg = strprintf("Sigcache testflags %i: xx", n); | |||||
uint256 hashMsg = Hash(strMsg.begin(), strMsg.end()); | |||||
std::vector<uint8_t> sig; | |||||
BOOST_CHECK(key1.SignECDSA(hashMsg, sig)); | |||||
// choose random background flagset to test | |||||
uint32_t base_flags = insecure_rand(); | |||||
// shouldn't be in cache at start | |||||
BOOST_CHECK(!testChecker.IsCached(sig, pubkey1, hashMsg, base_flags)); | |||||
// insert into cache | |||||
BOOST_CHECK( | |||||
testChecker.VerifyAndStore(sig, pubkey1, hashMsg, base_flags)); | |||||
// check that it's in | |||||
BOOST_CHECK(testChecker.IsCached(sig, pubkey1, hashMsg, base_flags)); | |||||
// now we flip each of 32 flags one by one, checking cache | |||||
for (uint32_t flag = 1; flag; flag <<= 1) { | |||||
uint32_t alt_flags = base_flags ^ flag; | |||||
BOOST_CHECK(alt_flags != base_flags); | |||||
bool hit = testChecker.IsCached(sig, pubkey1, hashMsg, alt_flags); | |||||
if (VARIANT_FLAGS & flag) { | |||||
// if it's in VARIANT_FLAGS, we must miss cache | |||||
BOOST_CHECK_MESSAGE(!hit, n << " bad cache hit 0x" << std::hex | |||||
<< base_flags << " ^ 0x" << flag); | |||||
} else if (INVARIANT_FLAGS & flag) { | |||||
// if it's in INVARIANT_FLAGS, we must hit cache | |||||
BOOST_CHECK_MESSAGE(hit, n << " bad cache miss 0x" << std::hex | |||||
<< base_flags << " ^ 0x" << flag); | |||||
} else { | |||||
// if it's in neither, don't care. | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |
Pick a different name in the test and the code.