diff --git a/src/init.cpp b/src/init.cpp index ccbaad7372..396fcf0f5a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1186,6 +1186,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) InitSignatureCache(); InitRangeproofCache(); + InitSurjectionproofCache(); LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); if (nScriptCheckThreads) { diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 67abf397c4..f913da8529 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -91,6 +91,9 @@ static CSignatureCache signatureCache; static CSignatureCache rangeProofCache; +static CSignatureCache surjectionProofCache; + + } // To be called once in AppInit2/TestingSetup to initialize the signatureCache @@ -115,6 +118,18 @@ void InitRangeproofCache() (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems); } +// To be called once in AppInit2/TestingSetup to initialize the surjectionrproof cache +void InitSurjectionproofCache() +{ + // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero, + // setup_bytes creates the minimum possible cache (2 elements). + size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE)), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); + size_t nElems = surjectionProofCache.setup_bytes(nMaxCacheSize); + LogPrintf("Using %zu MiB out of %zu requested for surjectionproof cache, able to store %zu elements\n", + (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems); +} + + bool CachingTransactionSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const { uint256 entry; @@ -153,3 +168,29 @@ bool CachingRangeProofChecker::VerifyRangeProof(const std::vector return true; } + +bool CachingSurjectionProofChecker::VerifySurjectionProof(secp256k1_surjectionproof& proof, std::vector& vTags, secp256k1_generator& gen, const secp256k1_context* secp256k1_ctx_verify_amounts) const +{ + std::vector vchproof; + size_t proof_len = 0; + vchproof.resize(secp256k1_surjectionproof_serialized_size(secp256k1_ctx_verify_amounts, &proof)); + secp256k1_surjectionproof_serialize(secp256k1_ctx_verify_amounts, &vchproof[0], &proof_len, &proof); + + std::vector vchGen; + vchGen.resize(CTxOutValue::nCommittedSize); + secp256k1_generator_serialize(secp256k1_ctx_verify_amounts, &vchGen[0], &gen); + + CPubKey pubkey(vchGen); + uint256 entry; + surjectionProofCache.ComputeEntry(entry, uint256(), vchproof, pubkey); + + if (surjectionProofCache.Get(entry, !store)) { + return true; + } + + if (secp256k1_surjectionproof_verify(secp256k1_ctx_verify_amounts, &proof, vTags.data(), vTags.size(), &gen) != 1) { + return false; + } + + return true; +} diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 1297c39392..0282d9845c 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -10,6 +10,7 @@ #include #include +#include #include // DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit @@ -45,7 +46,21 @@ public: }; +class CachingSurjectionProofChecker +{ +private: + bool store; +public: + CachingSurjectionProofChecker(bool storeIn){ + store = storeIn; + }; + + bool VerifySurjectionProof(secp256k1_surjectionproof& proof, std::vector& vTags, secp256k1_generator& gen, const secp256k1_context* ctx) const; + +}; + void InitSignatureCache(); void InitRangeproofCache(); +void InitSurjectionproofCache(); #endif // BITCOIN_SCRIPT_SIGCACHE_H diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index f27bb8fc44..e4e59e259c 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -44,6 +44,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) SetupNetworking(); InitSignatureCache(); InitRangeproofCache(); + InitSurjectionproofCache(); fPrintToDebugLog = false; // don't want to write to debug.log file fCheckBlockIndex = true; SelectParams(chainName); diff --git a/src/validation.cpp b/src/validation.cpp index 2d745304c7..765bd9059d 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -603,8 +603,9 @@ private: secp256k1_surjectionproof proof; std::vector vTags; secp256k1_generator gen; + const bool store; public: - CSurjectionCheck(secp256k1_surjectionproof& proofIn, std::vector& vTags_, secp256k1_generator& genIn) : proof(proofIn), gen(genIn) { + CSurjectionCheck(secp256k1_surjectionproof& proofIn, std::vector& vTags_, secp256k1_generator& genIn, const bool storeIn) : proof(proofIn), gen(genIn), store(storeIn) { vTags.swap(vTags_); } @@ -644,11 +645,7 @@ bool CBalanceCheck::operator()() bool CSurjectionCheck::operator()() { - if (secp256k1_surjectionproof_verify(secp256k1_ctx_verify_amounts, &proof, vTags.data(), vTags.size(), &gen) != 1) { - return false; - } - - return true; + return CachingSurjectionProofChecker(store).VerifySurjectionProof(proof, vTags, gen, secp256k1_ctx_verify_amounts); } } // namespace @@ -837,7 +834,7 @@ bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, const C if (secp256k1_surjectionproof_parse(secp256k1_ctx_verify_amounts, &proof, &asset.vchSurjectionproof[0], asset.vchSurjectionproof.size()) != 1) return false; - if (!QueueCheck(pvChecks, new CSurjectionCheck(proof, ephemeral_input_tags, gen))) { + if (!QueueCheck(pvChecks, new CSurjectionCheck(proof, ephemeral_input_tags, gen, cacheStore))) { return false; } // Each CSurjectionCheck uses swap to keep pointers valid.