Skip to content

Commit 7312eff

Browse files
committed
Merge bitcoin/bitcoin#25527: [kernel 3c/n] Decouple validation cache initialization from ArgsManager
0f3a253 validationcaches: Use size_t for sizes (Carl Dong) 41c5201 validationcaches: Add and use ValidationCacheSizes (Carl Dong) 82d3058 cuckoocache: Check for uint32 overflow in setup_bytes (Carl Dong) b370164 validationcaches: Abolish arbitrary limit (Carl Dong) 08dbc6e cuckoocache: Return approximate memory size (Carl Dong) 0dbce4b tests: Reduce calls to InitS*Cache() (Carl Dong) Pull request description: This is part of the `libbitcoinkernel` project: #24303, https://github.com/bitcoin/bitcoin/projects/18 This PR is **_NOT_** dependent on any other PRs. ----- a.k.a. "Stop calling `gArgs.GetIntArg("-maxsigcachesize")` from validation code" This PR introduces the `ValidationCacheSizes` struct and its corresponding `ApplyArgsManOptions` function, removing the need to call `gArgs` from `Init{Signature,ScriptExecution}Cache()`. This serves to further decouple `ArgsManager` from `libbitcoinkernel` code. More context can be gleaned from the commit messages. ACKs for top commit: glozow: re ACK 0f3a253 theStack: Code-review ACK 0f3a253 ryanofsky: Code review ACK 0f3a253. Rebase and comment tweak since last Tree-SHA512: a492ca608466979807cac25ae3d8ef75d2f1345de52a156aa0d222c5a940f79f1b65db40090de69183cccdb12297ec060f6c64e57a26a155a94fec80e07ea0f7
2 parents f765d4e + 0f3a253 commit 7312eff

15 files changed

+152
-44
lines changed

ci/test/06_script_b.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ if [ "${RUN_TIDY}" = "true" ]; then
4646
" src/kernel"\
4747
" src/node/chainstate.cpp"\
4848
" src/node/mempool_args.cpp"\
49+
" src/node/validation_cache_args.cpp"\
4950
" src/policy/feerate.cpp"\
5051
" src/policy/packages.cpp"\
5152
" src/policy/settings.cpp"\

src/Makefile.am

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ BITCOIN_CORE_H = \
177177
kernel/mempool_limits.h \
178178
kernel/mempool_options.h \
179179
kernel/mempool_persist.h \
180+
kernel/validation_cache_sizes.h \
180181
key.h \
181182
key_io.h \
182183
logging.h \
@@ -207,6 +208,7 @@ BITCOIN_CORE_H = \
207208
node/psbt.h \
208209
node/transaction.h \
209210
node/utxo_snapshot.h \
211+
node/validation_cache_args.h \
210212
noui.h \
211213
outputtype.h \
212214
policy/feerate.h \
@@ -390,6 +392,7 @@ libbitcoin_node_a_SOURCES = \
390392
node/minisketchwrapper.cpp \
391393
node/psbt.cpp \
392394
node/transaction.cpp \
395+
node/validation_cache_args.cpp \
393396
noui.cpp \
394397
policy/fees.cpp \
395398
policy/fees_args.cpp \

src/bitcoin-chainstate.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <kernel/checks.h>
1515
#include <kernel/context.h>
16+
#include <kernel/validation_cache_sizes.h>
1617

1718
#include <chainparams.h>
1819
#include <consensus/validation.h>
@@ -62,8 +63,9 @@ int main(int argc, char* argv[])
6263
// Necessary for CheckInputScripts (eventually called by ProcessNewBlock),
6364
// which will try the script cache first and fall back to actually
6465
// performing the check with the signature cache.
65-
InitSignatureCache();
66-
InitScriptExecutionCache();
66+
kernel::ValidationCacheSizes validation_cache_sizes{};
67+
Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
68+
Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
6769

6870

6971
// SETUP: Scheduling and Background Signals

src/cuckoocache.h

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
#include <atomic>
1313
#include <cmath>
1414
#include <cstring>
15+
#include <limits>
1516
#include <memory>
17+
#include <optional>
1618
#include <utility>
1719
#include <vector>
1820

@@ -326,7 +328,7 @@ class cache
326328
}
327329

328330
/** setup initializes the container to store no more than new_size
329-
* elements.
331+
* elements and no less than 2 elements.
330332
*
331333
* setup should only be called once.
332334
*
@@ -336,8 +338,8 @@ class cache
336338
uint32_t setup(uint32_t new_size)
337339
{
338340
// depth_limit must be at least one otherwise errors can occur.
339-
depth_limit = static_cast<uint8_t>(std::log2(static_cast<float>(std::max((uint32_t)2, new_size))));
340341
size = std::max<uint32_t>(2, new_size);
342+
depth_limit = static_cast<uint8_t>(std::log2(static_cast<float>(size)));
341343
table.resize(size);
342344
collection_flags.setup(size);
343345
epoch_flags.resize(size);
@@ -357,12 +359,21 @@ class cache
357359
*
358360
* @param bytes the approximate number of bytes to use for this data
359361
* structure
360-
* @returns the maximum number of elements storable (see setup()
361-
* documentation for more detail)
362+
* @returns A pair of the maximum number of elements storable (see setup()
363+
* documentation for more detail) and the approxmiate total size of these
364+
* elements in bytes or std::nullopt if the size requested is too large.
362365
*/
363-
uint32_t setup_bytes(size_t bytes)
366+
std::optional<std::pair<uint32_t, size_t>> setup_bytes(size_t bytes)
364367
{
365-
return setup(bytes/sizeof(Element));
368+
size_t requested_num_elems = bytes / sizeof(Element);
369+
if (std::numeric_limits<uint32_t>::max() < requested_num_elems) {
370+
return std::nullopt;
371+
}
372+
373+
auto num_elems = setup(bytes/sizeof(Element));
374+
375+
size_t approx_size_bytes = num_elems * sizeof(Element);
376+
return std::make_pair(num_elems, approx_size_bytes);
366377
}
367378

368379
/** insert loops at most depth_limit times trying to insert a hash

src/init.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <kernel/checks.h>
1313
#include <kernel/mempool_persist.h>
14+
#include <kernel/validation_cache_sizes.h>
1415

1516
#include <addrman.h>
1617
#include <banman.h>
@@ -44,6 +45,7 @@
4445
#include <node/mempool_args.h>
4546
#include <node/mempool_persist_args.h>
4647
#include <node/miner.h>
48+
#include <node/validation_cache_args.h>
4749
#include <policy/feerate.h>
4850
#include <policy/fees.h>
4951
#include <policy/fees_args.h>
@@ -105,7 +107,9 @@
105107
#endif
106108

107109
using kernel::DumpMempool;
110+
using kernel::ValidationCacheSizes;
108111

112+
using node::ApplyArgsManOptions;
109113
using node::CacheSizes;
110114
using node::CalculateCacheSizes;
111115
using node::DEFAULT_PERSIST_MEMPOOL;
@@ -548,7 +552,7 @@ void SetupServerArgs(ArgsManager& argsman)
548552
argsman.AddArg("-addrmantest", "Allows to test address relay on localhost", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
549553
argsman.AddArg("-capturemessages", "Capture all P2P messages to disk", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
550554
argsman.AddArg("-mocktime=<n>", "Replace actual time with " + UNIX_EPOCH_TIME + " (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
551-
argsman.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
555+
argsman.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_BYTES >> 20), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
552556
argsman.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
553557
argsman.AddArg("-printpriority", strprintf("Log transaction fee rate in " + CURRENCY_UNIT + "/kvB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
554558
argsman.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
@@ -1115,8 +1119,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
11151119
args.GetArg("-datadir", ""), fs::PathToString(fs::current_path()));
11161120
}
11171121

1118-
InitSignatureCache();
1119-
InitScriptExecutionCache();
1122+
ValidationCacheSizes validation_cache_sizes{};
1123+
ApplyArgsManOptions(args, validation_cache_sizes);
1124+
if (!InitSignatureCache(validation_cache_sizes.signature_cache_bytes)
1125+
|| !InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes))
1126+
{
1127+
return InitError(strprintf(_("Unable to allocate memory for -maxsigcachesize: '%s' MiB"), args.GetIntArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_BYTES >> 20)));
1128+
}
11201129

11211130
int script_threads = args.GetIntArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
11221131
if (script_threads <= 0) {

src/kernel/validation_cache_sizes.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) 2022 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_KERNEL_VALIDATION_CACHE_SIZES_H
6+
#define BITCOIN_KERNEL_VALIDATION_CACHE_SIZES_H
7+
8+
#include <script/sigcache.h>
9+
10+
#include <cstddef>
11+
#include <limits>
12+
13+
namespace kernel {
14+
struct ValidationCacheSizes {
15+
size_t signature_cache_bytes{DEFAULT_MAX_SIG_CACHE_BYTES / 2};
16+
size_t script_execution_cache_bytes{DEFAULT_MAX_SIG_CACHE_BYTES / 2};
17+
};
18+
}
19+
20+
#endif // BITCOIN_KERNEL_VALIDATION_CACHE_SIZES_H

src/node/validation_cache_args.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2022 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <node/validation_cache_args.h>
6+
7+
#include <kernel/validation_cache_sizes.h>
8+
9+
#include <util/system.h>
10+
11+
#include <algorithm>
12+
#include <cstddef>
13+
#include <cstdint>
14+
#include <memory>
15+
#include <optional>
16+
17+
using kernel::ValidationCacheSizes;
18+
19+
namespace node {
20+
void ApplyArgsManOptions(const ArgsManager& argsman, ValidationCacheSizes& cache_sizes)
21+
{
22+
if (auto max_size = argsman.GetIntArg("-maxsigcachesize")) {
23+
// 1. When supplied with a max_size of 0, both InitSignatureCache and
24+
// InitScriptExecutionCache create the minimum possible cache (2
25+
// elements). Therefore, we can use 0 as a floor here.
26+
// 2. Multiply first, divide after to avoid integer truncation.
27+
size_t clamped_size_each = std::max<int64_t>(*max_size, 0) * (1 << 20) / 2;
28+
cache_sizes = {
29+
.signature_cache_bytes = clamped_size_each,
30+
.script_execution_cache_bytes = clamped_size_each,
31+
};
32+
}
33+
}
34+
} // namespace node

src/node/validation_cache_args.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (c) 2022 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_NODE_VALIDATION_CACHE_ARGS_H
6+
#define BITCOIN_NODE_VALIDATION_CACHE_ARGS_H
7+
8+
class ArgsManager;
9+
namespace kernel {
10+
struct ValidationCacheSizes;
11+
};
12+
13+
namespace node {
14+
void ApplyArgsManOptions(const ArgsManager& argsman, kernel::ValidationCacheSizes& cache_sizes);
15+
} // namespace node
16+
17+
#endif // BITCOIN_NODE_VALIDATION_CACHE_ARGS_H

src/script/sigcache.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <algorithm>
1616
#include <mutex>
17+
#include <optional>
1718
#include <shared_mutex>
1819
#include <vector>
1920

@@ -75,7 +76,7 @@ class CSignatureCache
7576
std::unique_lock<std::shared_mutex> lock(cs_sigcache);
7677
setValid.insert(entry);
7778
}
78-
uint32_t setup_bytes(size_t n)
79+
std::optional<std::pair<uint32_t, size_t>> setup_bytes(size_t n)
7980
{
8081
return setValid.setup_bytes(n);
8182
}
@@ -92,14 +93,15 @@ static CSignatureCache signatureCache;
9293

9394
// To be called once in AppInitMain/BasicTestingSetup to initialize the
9495
// signatureCache.
95-
void InitSignatureCache()
96+
bool InitSignatureCache(size_t max_size_bytes)
9697
{
97-
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
98-
// setup_bytes creates the minimum possible cache (2 elements).
99-
size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetIntArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
100-
size_t nElems = signatureCache.setup_bytes(nMaxCacheSize);
101-
LogPrintf("Using %zu MiB out of %zu/2 requested for signature cache, able to store %zu elements\n",
102-
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
98+
auto setup_results = signatureCache.setup_bytes(max_size_bytes);
99+
if (!setup_results) return false;
100+
101+
const auto [num_elems, approx_size_bytes] = *setup_results;
102+
LogPrintf("Using %zu MiB out of %zu MiB requested for signature cache, able to store %zu elements\n",
103+
approx_size_bytes >> 20, max_size_bytes >> 20, num_elems);
104+
return true;
103105
}
104106

105107
bool CachingTransactionSignatureChecker::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const

src/script/sigcache.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@
1010
#include <span.h>
1111
#include <util/hasher.h>
1212

13+
#include <optional>
1314
#include <vector>
1415

15-
// DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit
16+
// DoS prevention: limit cache size to 32MiB (over 1000000 entries on 64-bit
1617
// systems). Due to how we count cache size, actual memory usage is slightly
17-
// more (~32.25 MB)
18-
static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 32;
19-
// Maximum sig cache size allowed
20-
static const int64_t MAX_MAX_SIG_CACHE_SIZE = 16384;
18+
// more (~32.25 MiB)
19+
static constexpr size_t DEFAULT_MAX_SIG_CACHE_BYTES{32 << 20};
2120

2221
class CPubKey;
2322

@@ -33,6 +32,6 @@ class CachingTransactionSignatureChecker : public TransactionSignatureChecker
3332
bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const override;
3433
};
3534

36-
void InitSignatureCache();
35+
[[nodiscard]] bool InitSignatureCache(size_t max_size_bytes);
3736

3837
#endif // BITCOIN_SCRIPT_SIGCACHE_H

0 commit comments

Comments
 (0)