diff --git a/src/common/data_cache.h b/src/common/data_cache.h new file mode 100644 index 000000000..5e70da115 --- /dev/null +++ b/src/common/data_cache.h @@ -0,0 +1,65 @@ +// Copyright (c) 2014-2022, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include +#include + +namespace tools +{ + template + class data_cache + { + public: + void add(const T& value) + { + std::lock_guard lock(m); + if (data.insert(value).second) + { + T& old_value = buf[counter++ % MAX_SIZE]; + data.erase(old_value); + old_value = value; + } + } + + bool has(const T& value) const + { + std::lock_guard lock(m); + return (data.find(value) != data.end()); + } + + private: + mutable std::mutex m; + std::unordered_set data; + T buf[MAX_SIZE] = {}; + size_t counter = 0; + }; +} diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 135dd3df0..d312d97bd 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3613,7 +3613,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (!rct::verRctNonSemanticsSimple(rv)) + if (!rct::verRctNonSemanticsSimpleCached(rv)) { MERROR_VER("Failed to check ringct signatures!"); return false; diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 477a7907d..7b16f017b 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -30,6 +30,7 @@ #include "misc_log_ex.h" #include "misc_language.h" +#include "common/data_cache.h" #include "common/perf_timer.h" #include "common/threadpool.h" #include "common/util.h" @@ -1578,6 +1579,42 @@ namespace rct { } } + bool verRctNonSemanticsSimpleCached(const rctSig & rv) + { + // Hello future Monero dev! If you got this assert, read the following carefully: + // + // RCT cache assumes that this function will serialize and hash all rv's fields used for RingCT verification + // If you're about to add a new RCTType here, first you must check that binary_archive serialization writes all rv's fields to the binary blob + // If it's not the case, rewrite this function to serialize everything, even some "temporary" fields which are not serialized normally + CHECK_AND_ASSERT_MES_L1(rv.type <= RCTTypeBulletproofPlus, false, "Unknown RCT type. Make sure RCT cache works correctly with this type and then enable it in the code here."); + + // Don't cache older (or newer) rctSig types + // This cache only makes sense when it caches data from mempool first, + // so only "current fork version-enabled" RCT types need to be cached + if (rv.type != RCTTypeBulletproofPlus) + return verRctNonSemanticsSimple(rv); + + // Get the hash of rv + std::stringstream ss; + binary_archive ar(ss); + + ::do_serialize(ar, const_cast(rv)); + + crypto::hash h; + cryptonote::get_blob_hash(ss.str(), h); + + static tools::data_cache cache; + + if (cache.has(h)) + return true; + + const bool res = verRctNonSemanticsSimple(rv); + if (res) + cache.add(h); + + return res; + } + //RingCT protocol //genRct: // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 17cfd77b9..18c7e5fe6 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -132,6 +132,7 @@ namespace rct { bool verRctSemanticsSimple(const rctSig & rv); bool verRctSemanticsSimple(const std::vector & rv); bool verRctNonSemanticsSimple(const rctSig & rv); + bool verRctNonSemanticsSimpleCached(const rctSig & rv); static inline bool verRctSimple(const rctSig & rv) { return verRctSemanticsSimple(rv) && verRctNonSemanticsSimple(rv); } xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);