Skip to content

Commit

Permalink
Merge #13204: Faster sigcache nonce
Browse files Browse the repository at this point in the history
152e8ba Use salted hasher instead of nonce in sigcache (Jeremy Rubin)
5495fa5 Add Hash Padding Microbenchmarks (Jeremy Rubin)

Pull request description:

  This PR replaces nonces in two places with pre-salted hashers.

  The nonce is chosen to be 64 bytes long so that it forces the SHA256 hasher to process the chunk. This leaves the next 64 (or 56 depending if final chunk) open for data. In the case of the script execution cache, this does not make a big performance improvement because the nonce was already properly padded to fit into one buffer, but does make the code a little simpler. In the case of the sig cache, this should reduce the hashing overhead slightly because we are less likely to need an additional processing step.

  I haven't benchmarked this, but back of the envelope it should reduce the hashing by one buffer for all combinations except compressed public keys with compact signatures.

ACKs for top commit:
  ryanofsky:
    Code review ACK 152e8ba. No code changes, just rebase since last review and expanded commit message

Tree-SHA512: b133e902fd595cfe3b54ad8814b823f4d132cb2c358c89158842ae27daee56ab5f70cde2585078deb46f77a6e7b35b4cc6bba47b65302b7befc2cff254bad93d
  • Loading branch information
MarcoFalke committed Jun 2, 2020
2 parents 45a1489 + 152e8ba commit 9e8bd21
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/Makefile.bench.include
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ bench_bench_bitcoin_SOURCES = \
bench/crypto_hash.cpp \
bench/ccoins_caching.cpp \
bench/gcs_filter.cpp \
bench/hashpadding.cpp \
bench/merkle_root.cpp \
bench/mempool_eviction.cpp \
bench/mempool_stress.cpp \
Expand Down
47 changes: 47 additions & 0 deletions src/bench/hashpadding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) 2015-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <bench/bench.h>
#include <hash.h>
#include <random.h>
#include <uint256.h>


static void PrePadded(benchmark::State& state)
{

CSHA256 hasher;

// Setup the salted hasher
uint256 nonce = GetRandHash();
hasher.Write(nonce.begin(), 32);
hasher.Write(nonce.begin(), 32);
uint256 data = GetRandHash();
while (state.KeepRunning()) {
unsigned char out[32];
CSHA256 h = hasher;
h.Write(data.begin(), 32);
h.Finalize(out);
}
}

BENCHMARK(PrePadded, 10000);

static void RegularPadded(benchmark::State& state)
{
CSHA256 hasher;

// Setup the salted hasher
uint256 nonce = GetRandHash();
uint256 data = GetRandHash();
while (state.KeepRunning()) {
unsigned char out[32];
CSHA256 h = hasher;
h.Write(nonce.begin(), 32);
h.Write(data.begin(), 32);
h.Finalize(out);
}
}

BENCHMARK(RegularPadded, 10000);
12 changes: 9 additions & 3 deletions src/script/sigcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,27 @@ class CSignatureCache
{
private:
//! Entries are SHA256(nonce || signature hash || public key || signature):
uint256 nonce;
CSHA256 m_salted_hasher;
typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
map_type setValid;
boost::shared_mutex cs_sigcache;

public:
CSignatureCache()
{
GetRandBytes(nonce.begin(), 32);
uint256 nonce = GetRandHash();
// We want the nonce to be 64 bytes long to force the hasher to process
// this chunk, which makes later hash computations more efficient. We
// just write our 32-byte entropy twice to fill the 64 bytes.
m_salted_hasher.Write(nonce.begin(), 32);
m_salted_hasher.Write(nonce.begin(), 32);
}

void
ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
{
CSHA256().Write(nonce.begin(), 32).Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
CSHA256 hasher = m_salted_hasher;
hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
}

bool
Expand Down
23 changes: 14 additions & 9 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1481,14 +1481,21 @@ int GetSpendHeight(const CCoinsViewCache& inputs)
}


static CuckooCache::cache<uint256, SignatureCacheHasher> scriptExecutionCache;
static uint256 scriptExecutionCacheNonce(GetRandHash());
static CuckooCache::cache<uint256, SignatureCacheHasher> g_scriptExecutionCache;
static CSHA256 g_scriptExecutionCacheHasher;

void InitScriptExecutionCache() {
// Setup the salted hasher
uint256 nonce = GetRandHash();
// We want the nonce to be 64 bytes long to force the hasher to process
// this chunk, which makes later hash computations more efficient. We
// just write our 32-byte entropy twice to fill the 64 bytes.
g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
// 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, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
size_t nElems = scriptExecutionCache.setup_bytes(nMaxCacheSize);
size_t nElems = g_scriptExecutionCache.setup_bytes(nMaxCacheSize);
LogPrintf("Using %zu MiB out of %zu/2 requested for script execution cache, able to store %zu elements\n",
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
}
Expand Down Expand Up @@ -1526,12 +1533,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
// properly commits to the scriptPubKey in the inputs view of that
// transaction).
uint256 hashCacheEntry;
// We only use the first 19 bytes of nonce to avoid a second SHA
// round - giving us 19 + 32 + 4 = 55 bytes (+ 8 + 1 = 64)
static_assert(55 - sizeof(flags) - 32 >= 128/8, "Want at least 128 bits of nonce for script execution cache");
CSHA256().Write(scriptExecutionCacheNonce.begin(), 55 - sizeof(flags) - 32).Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
CSHA256 hasher = g_scriptExecutionCacheHasher;
hasher.Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
if (scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
if (g_scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
return true;
}

Expand Down Expand Up @@ -1586,7 +1591,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
if (cacheFullScriptStore && !pvChecks) {
// We executed all of the provided scripts, and were told to
// cache the result. Do so now.
scriptExecutionCache.insert(hashCacheEntry);
g_scriptExecutionCache.insert(hashCacheEntry);
}

return true;
Expand Down

0 comments on commit 9e8bd21

Please sign in to comment.