From 66ab07bd1b8778e80bbc043ed13c9661c28d3bbe Mon Sep 17 00:00:00 2001 From: instagibbs Date: Tue, 12 Jul 2016 11:02:06 -0400 Subject: [PATCH] Rangeproof caching --- src/init.cpp | 1 + src/script/sigcache.cpp | 32 ++++++++++++++++++++++++++++++++ src/script/sigcache.h | 16 ++++++++++++++++ src/test/test_bitcoin.cpp | 1 + src/txmempool.cpp | 4 ++-- src/validation.cpp | 21 ++++++++------------- src/validation.h | 5 +++-- 7 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index a5a16972e9..3e66c19c60 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1185,6 +1185,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); InitSignatureCache(); + InitRangeproofCache(); LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); if (nScriptCheckThreads) { diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 6f364e42d1..c35b29645a 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -88,6 +88,9 @@ public: * signatureCache could be made local to VerifySignature. */ static CSignatureCache signatureCache; + +static CSignatureCache rangeProofCache; + } // To be called once in AppInit2/TestingSetup to initialize the signatureCache @@ -101,6 +104,17 @@ void InitSignatureCache() (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems); } +// To be called once in AppInit2/TestingSetup to initialize the rangeproof cache +void InitRangeproofCache() +{ + // 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 = rangeProofCache.setup_bytes(nMaxCacheSize); + LogPrintf("Using %zu MiB out of %zu requested for rangeproof 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; @@ -113,3 +127,21 @@ bool CachingTransactionSignatureChecker::VerifySignature(const std::vector& vchRangeProof, const std::vector& vchCommitment, const secp256k1_context* secp256k1_ctx_verify_amounts) const +{ + CPubKey pubkey(vchCommitment); + uint256 entry; + rangeProofCache.ComputeEntry(entry, uint256(), vchRangeProof, pubkey); + + if (rangeProofCache.Get(entry, !store)) { + return true; + } + + uint64_t min_value, max_value; + if (!secp256k1_rangeproof_verify(secp256k1_ctx_verify_amounts, &min_value, &max_value, &vchCommitment[0], vchRangeProof.data(), vchRangeProof.size())) { + return false; + } + + return true; +} diff --git a/src/script/sigcache.h b/src/script/sigcache.h index b8cb69d37e..c742462bc7 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -8,6 +8,8 @@ #include "script/interpreter.h" +#include +#include #include // DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit @@ -30,6 +32,20 @@ public: bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; +class CachingRangeProofChecker +{ +private: + bool store; +public: + CachingRangeProofChecker(bool storeIn){ + store = storeIn; + }; + + bool VerifyRangeProof(const std::vector& vchRangeProof, const std::vector& vchCommitment, const secp256k1_context* ctx) const; + +}; + void InitSignatureCache(); +void InitRangeproofCache(); #endif // BITCOIN_SCRIPT_SIGCACHE_H diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 6a507ed1cb..f27bb8fc44 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -43,6 +43,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) SetupEnvironment(); SetupNetworking(); InitSignatureCache(); + InitRangeproofCache(); fPrintToDebugLog = false; // don't want to write to debug.log file fCheckBlockIndex = true; SelectParams(chainName); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 6a3ed559c1..115bb81228 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -759,7 +759,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const CValidationState state; std::set > setWithdrawsSpent; bool fCheckResult = tx.IsCoinBase() || - Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight, setWithdrawsSpent, NULL); + Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight, setWithdrawsSpent, NULL, false); assert(fCheckResult); UpdateCoins(tx, mempoolDuplicate, 1000000); assert(setWithdrawsSpent == it->setWithdrawsSpent); @@ -780,7 +780,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const } else { std::set > setWithdrawsSpent; bool fCheckResult = entry->GetTx().IsCoinBase() || - Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight, setWithdrawsSpent, NULL); + Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight, setWithdrawsSpent, NULL, false); assert(fCheckResult); UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); assert(setWithdrawsSpent == entry->setWithdrawsSpent); diff --git a/src/validation.cpp b/src/validation.cpp index 25f5bc7153..e19084724d 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -573,9 +573,10 @@ class CRangeCheck : public CCheck { private: const CTxOutValue* val; + const bool store; public: - CRangeCheck(const CTxOutValue* val_) : val(val_) {} + CRangeCheck(const CTxOutValue* val_, const bool storeIn) : val(val_), store(storeIn) {} bool operator()(); }; @@ -617,13 +618,7 @@ bool CRangeCheck::operator()() return true; } - uint64_t min_value, max_value; - if (!secp256k1_rangeproof_verify(secp256k1_ctx_verify_amounts, &min_value, &max_value, &val->vchCommitment[0], val->vchRangeproof.data(), val->vchRangeproof.size())) { - fAmountError = true; - return false; - } - - return true; + return CachingRangeProofChecker(store).VerifyRangeProof(val->vchRangeproof, val->vchCommitment, secp256k1_ctx_verify_amounts); }; bool CBalanceCheck::operator()() @@ -640,7 +635,7 @@ bool CBalanceCheck::operator()() -bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, const CAmount& excess, std::vector* pvChecks) +bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, const CAmount& excess, std::vector* pvChecks, const bool cacheStore) { bool fNeedNoRangeProof = false; CAmount nPlainAmount = excess; @@ -712,7 +707,7 @@ bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, const C const CTxOutValue& val = tx.vout[i].nValue; if (val.IsAmount()) continue; - if (!QueueCheck(pvChecks, new CRangeCheck(&val))) { + if (!QueueCheck(pvChecks, new CRangeCheck(&val, cacheStore))) { return false; } } @@ -1711,7 +1706,7 @@ int GetSpendHeight(const CCoinsViewCache& inputs) } namespace Consensus { -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, std::set >& setWithdrawsSpent, std::vector *pvChecks) +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, std::set >& setWithdrawsSpent, std::vector *pvChecks, const bool cacheStore) { // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. @@ -1763,7 +1758,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins if (!MoneyRange(nFees)) return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); - if (!VerifyAmounts(inputs, tx, nTxFee, pvChecks)) + if (!VerifyAmounts(inputs, tx, nTxFee, pvChecks, cacheStore)) return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, strprintf("value in (%s) < value out", FormatMoney(nValueIn))); @@ -1775,7 +1770,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi { if (!tx.IsCoinBase()) { - if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), setWithdrawsSpent, pvChecks)) + if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), setWithdrawsSpent, pvChecks, cacheStore)) return false; if (pvChecks) diff --git a/src/validation.h b/src/validation.h index 0542b74a2d..b655b4ac9b 100644 --- a/src/validation.h +++ b/src/validation.h @@ -391,7 +391,7 @@ namespace Consensus { * This does not modify the UTXO set. This does not check scripts and sigs. * Preconditions: tx.IsCoinBase() is false. */ -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, std::set >& setWithdrawsSpent, std::vector *pvChecks); +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, std::set >& setWithdrawsSpent, std::vector *pvChecks, const bool cacheStore); } // namespace Consensus @@ -402,9 +402,10 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins * @param[in] tx transaction for which we are checking totals * @param[in] excess additional amount to consider as input value (eg fees), can be negative * @param[in] pvChecks multithreaded rangeproof and commitment checker + * @param[in] cacheStore signal if rangeproof verification should be cached * @return True if totals are identical */ -bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, const CAmount& excess, std::vector* pvChecks = NULL); +bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, const CAmount& excess, std::vector* pvChecks = NULL, const bool cacheStore = false); /**