aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.bench.include1
-rw-r--r--src/bench/crypto_hash.cpp18
-rw-r--r--src/bench/random.cpp103
-rw-r--r--src/bitcoin-chainstate.cpp8
-rw-r--r--src/cuckoocache.h14
-rw-r--r--src/httpserver.h2
-rw-r--r--src/init.cpp13
-rw-r--r--src/kernel/chainparams.cpp9
-rw-r--r--src/kernel/chainstatemanager_opts.h3
-rw-r--r--src/kernel/validation_cache_sizes.h20
-rw-r--r--src/net.cpp12
-rw-r--r--src/net.h2
-rw-r--r--src/net_processing.cpp13
-rw-r--r--src/node/blockstorage.cpp8
-rw-r--r--src/node/blockstorage.h31
-rw-r--r--src/node/chainstatemanager_args.cpp10
-rw-r--r--src/node/validation_cache_args.cpp34
-rw-r--r--src/node/validation_cache_args.h17
-rw-r--r--src/node/warnings.cpp3
-rw-r--r--src/psbt.h9
-rw-r--r--src/random.h23
-rw-r--r--src/rpc/blockchain.cpp38
-rw-r--r--src/rpc/blockchain.h4
-rw-r--r--src/rpc/rawtransaction.cpp4
-rw-r--r--src/script/sigcache.cpp131
-rw-r--r--src/script/sigcache.h51
-rw-r--r--src/test/blockchain_tests.cpp34
-rw-r--r--src/test/blockmanager_tests.cpp5
-rw-r--r--src/test/fuzz/fuzz.cpp4
-rw-r--r--src/test/fuzz/random.cpp1
-rw-r--r--src/test/fuzz/script_sigcache.cpp5
-rw-r--r--src/test/fuzz/string.cpp3
-rw-r--r--src/test/fuzz/utxo_snapshot.cpp46
-rw-r--r--src/test/miniscript_tests.cpp2
-rw-r--r--src/test/net_peer_eviction_tests.cpp4
-rw-r--r--src/test/random_tests.cpp6
-rw-r--r--src/test/script_p2sh_tests.cpp3
-rw-r--r--src/test/script_tests.cpp9
-rw-r--r--src/test/transaction_tests.cpp5
-rw-r--r--src/test/txpackage_tests.cpp2
-rw-r--r--src/test/txrequest_tests.cpp4
-rw-r--r--src/test/txvalidationcache_tests.cpp36
-rw-r--r--src/test/util/net.cpp28
-rw-r--r--src/test/util/setup_common.cpp9
-rw-r--r--src/util/translation.h12
-rw-r--r--src/validation.cpp75
-rw-r--r--src/validation.h36
-rw-r--r--src/wallet/coinselection.cpp6
-rw-r--r--src/wallet/scriptpubkeyman.cpp78
-rw-r--r--src/wallet/scriptpubkeyman.h168
-rw-r--r--src/wallet/spend.cpp2
-rw-r--r--src/wallet/sqlite.cpp2
-rw-r--r--src/wallet/wallet.cpp58
-rw-r--r--src/wallet/wallet.h4
-rw-r--r--src/wallet/walletdb.cpp26
56 files changed, 741 insertions, 517 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 183d196a7b..0ae5effdbe 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -195,7 +195,6 @@ BITCOIN_CORE_H = \
kernel/mempool_removal_reason.h \
kernel/messagestartchars.h \
kernel/notifications_interface.h \
- kernel/validation_cache_sizes.h \
kernel/warning.h \
key.h \
key_io.h \
@@ -240,7 +239,6 @@ BITCOIN_CORE_H = \
node/txreconciliation.h \
node/types.h \
node/utxo_snapshot.h \
- node/validation_cache_args.h \
node/warnings.h \
noui.h \
outputtype.h \
@@ -445,7 +443,6 @@ libbitcoin_node_a_SOURCES = \
node/transaction.cpp \
node/txreconciliation.cpp \
node/utxo_snapshot.cpp \
- node/validation_cache_args.cpp \
node/warnings.cpp \
noui.cpp \
policy/fees.cpp \
@@ -710,7 +707,6 @@ libbitcoin_common_a_SOURCES = \
outputtype.cpp \
policy/feerate.cpp \
policy/policy.cpp \
- policy/truc_policy.cpp \
protocol.cpp \
psbt.cpp \
rpc/external_signer.cpp \
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 2ba72c9e76..cd2626b330 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -49,6 +49,7 @@ bench_bench_bitcoin_SOURCES = \
bench/poly1305.cpp \
bench/pool.cpp \
bench/prevector.cpp \
+ bench/random.cpp \
bench/readblock.cpp \
bench/rollingbloom.cpp \
bench/rpc_blockchain.cpp \
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 1685a120b4..2551ff3593 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -196,22 +196,6 @@ static void SipHash_32b(benchmark::Bench& bench)
});
}
-static void FastRandom_32bit(benchmark::Bench& bench)
-{
- FastRandomContext rng(true);
- bench.run([&] {
- rng.rand32();
- });
-}
-
-static void FastRandom_1bit(benchmark::Bench& bench)
-{
- FastRandomContext rng(true);
- bench.run([&] {
- rng.randbool();
- });
-}
-
static void MuHash(benchmark::Bench& bench)
{
MuHash3072 acc;
@@ -274,8 +258,6 @@ BENCHMARK(SHA256D64_1024_STANDARD, benchmark::PriorityLevel::HIGH);
BENCHMARK(SHA256D64_1024_SSE4, benchmark::PriorityLevel::HIGH);
BENCHMARK(SHA256D64_1024_AVX2, benchmark::PriorityLevel::HIGH);
BENCHMARK(SHA256D64_1024_SHANI, benchmark::PriorityLevel::HIGH);
-BENCHMARK(FastRandom_32bit, benchmark::PriorityLevel::HIGH);
-BENCHMARK(FastRandom_1bit, benchmark::PriorityLevel::HIGH);
BENCHMARK(MuHash, benchmark::PriorityLevel::HIGH);
BENCHMARK(MuHashMul, benchmark::PriorityLevel::HIGH);
diff --git a/src/bench/random.cpp b/src/bench/random.cpp
new file mode 100644
index 0000000000..cff215d5a7
--- /dev/null
+++ b/src/bench/random.cpp
@@ -0,0 +1,103 @@
+// Copyright (c) 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 <random.h>
+
+#include <cstdint>
+#include <numeric>
+
+namespace {
+
+template<typename RNG>
+void BenchRandom_rand64(benchmark::Bench& bench, RNG&& rng) noexcept
+{
+ bench.batch(1).unit("number").run([&] {
+ rng.rand64();
+ });
+}
+
+template<typename RNG>
+void BenchRandom_rand32(benchmark::Bench& bench, RNG&& rng) noexcept
+{
+ bench.batch(1).unit("number").run([&] {
+ rng.rand32();
+ });
+}
+
+template<typename RNG>
+void BenchRandom_randbool(benchmark::Bench& bench, RNG&& rng) noexcept
+{
+ bench.batch(1).unit("number").run([&] {
+ rng.randbool();
+ });
+}
+
+template<typename RNG>
+void BenchRandom_randbits(benchmark::Bench& bench, RNG&& rng) noexcept
+{
+ bench.batch(64).unit("number").run([&] {
+ for (int i = 1; i <= 64; ++i) {
+ rng.randbits(i);
+ }
+ });
+}
+
+template<int RANGE, typename RNG>
+void BenchRandom_randrange(benchmark::Bench& bench, RNG&& rng) noexcept
+{
+ bench.batch(RANGE).unit("number").run([&] {
+ for (int i = 1; i <= RANGE; ++i) {
+ rng.randrange(i);
+ }
+ });
+}
+
+template<int RANGE, typename RNG>
+void BenchRandom_stdshuffle(benchmark::Bench& bench, RNG&& rng) noexcept
+{
+ uint64_t data[RANGE];
+ std::iota(std::begin(data), std::end(data), uint64_t(0));
+ bench.batch(RANGE).unit("number").run([&] {
+ std::shuffle(std::begin(data), std::end(data), rng);
+ });
+}
+
+void FastRandom_rand64(benchmark::Bench& bench) { BenchRandom_rand64(bench, FastRandomContext(true)); }
+void FastRandom_rand32(benchmark::Bench& bench) { BenchRandom_rand32(bench, FastRandomContext(true)); }
+void FastRandom_randbool(benchmark::Bench& bench) { BenchRandom_randbool(bench, FastRandomContext(true)); }
+void FastRandom_randbits(benchmark::Bench& bench) { BenchRandom_randbits(bench, FastRandomContext(true)); }
+void FastRandom_randrange100(benchmark::Bench& bench) { BenchRandom_randrange<100>(bench, FastRandomContext(true)); }
+void FastRandom_randrange1000(benchmark::Bench& bench) { BenchRandom_randrange<1000>(bench, FastRandomContext(true)); }
+void FastRandom_randrange1000000(benchmark::Bench& bench) { BenchRandom_randrange<1000000>(bench, FastRandomContext(true)); }
+void FastRandom_stdshuffle100(benchmark::Bench& bench) { BenchRandom_stdshuffle<100>(bench, FastRandomContext(true)); }
+
+void InsecureRandom_rand64(benchmark::Bench& bench) { BenchRandom_rand64(bench, InsecureRandomContext(251438)); }
+void InsecureRandom_rand32(benchmark::Bench& bench) { BenchRandom_rand32(bench, InsecureRandomContext(251438)); }
+void InsecureRandom_randbool(benchmark::Bench& bench) { BenchRandom_randbool(bench, InsecureRandomContext(251438)); }
+void InsecureRandom_randbits(benchmark::Bench& bench) { BenchRandom_randbits(bench, InsecureRandomContext(251438)); }
+void InsecureRandom_randrange100(benchmark::Bench& bench) { BenchRandom_randrange<100>(bench, InsecureRandomContext(251438)); }
+void InsecureRandom_randrange1000(benchmark::Bench& bench) { BenchRandom_randrange<1000>(bench, InsecureRandomContext(251438)); }
+void InsecureRandom_randrange1000000(benchmark::Bench& bench) { BenchRandom_randrange<1000000>(bench, InsecureRandomContext(251438)); }
+void InsecureRandom_stdshuffle100(benchmark::Bench& bench) { BenchRandom_stdshuffle<100>(bench, InsecureRandomContext(251438)); }
+
+} // namespace
+
+BENCHMARK(FastRandom_rand64, benchmark::PriorityLevel::HIGH);
+BENCHMARK(FastRandom_rand32, benchmark::PriorityLevel::HIGH);
+BENCHMARK(FastRandom_randbool, benchmark::PriorityLevel::HIGH);
+BENCHMARK(FastRandom_randbits, benchmark::PriorityLevel::HIGH);
+BENCHMARK(FastRandom_randrange100, benchmark::PriorityLevel::HIGH);
+BENCHMARK(FastRandom_randrange1000, benchmark::PriorityLevel::HIGH);
+BENCHMARK(FastRandom_randrange1000000, benchmark::PriorityLevel::HIGH);
+BENCHMARK(FastRandom_stdshuffle100, benchmark::PriorityLevel::HIGH);
+
+BENCHMARK(InsecureRandom_rand64, benchmark::PriorityLevel::HIGH);
+BENCHMARK(InsecureRandom_rand32, benchmark::PriorityLevel::HIGH);
+BENCHMARK(InsecureRandom_randbool, benchmark::PriorityLevel::HIGH);
+BENCHMARK(InsecureRandom_randbits, benchmark::PriorityLevel::HIGH);
+BENCHMARK(InsecureRandom_randrange100, benchmark::PriorityLevel::HIGH);
+BENCHMARK(InsecureRandom_randrange1000, benchmark::PriorityLevel::HIGH);
+BENCHMARK(InsecureRandom_randrange1000000, benchmark::PriorityLevel::HIGH);
+BENCHMARK(InsecureRandom_stdshuffle100, benchmark::PriorityLevel::HIGH);
diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp
index ecbdcd48bb..98af162b4d 100644
--- a/src/bitcoin-chainstate.cpp
+++ b/src/bitcoin-chainstate.cpp
@@ -15,7 +15,6 @@
#include <kernel/chainstatemanager_opts.h>
#include <kernel/checks.h>
#include <kernel/context.h>
-#include <kernel/validation_cache_sizes.h>
#include <kernel/warning.h>
#include <consensus/validation.h>
@@ -63,13 +62,6 @@ int main(int argc, char* argv[])
// properly
assert(kernel::SanityChecks(kernel_context));
- // Necessary for CheckInputScripts (eventually called by ProcessNewBlock),
- // which will try the script cache first and fall back to actually
- // performing the check with the signature cache.
- kernel::ValidationCacheSizes validation_cache_sizes{};
- Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
- Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
-
ValidationSignals validation_signals{std::make_unique<util::ImmediateTaskRunner>()};
class KernelNotifications : public kernel::Notifications
diff --git a/src/cuckoocache.h b/src/cuckoocache.h
index df320ed465..8370179395 100644
--- a/src/cuckoocache.h
+++ b/src/cuckoocache.h
@@ -14,7 +14,6 @@
#include <cstring>
#include <limits>
#include <memory>
-#include <optional>
#include <utility>
#include <vector>
@@ -360,16 +359,15 @@ public:
* structure
* @returns A pair of the maximum number of elements storable (see setup()
* documentation for more detail) and the approximate total size of these
- * elements in bytes or std::nullopt if the size requested is too large.
+ * elements in bytes.
*/
- std::optional<std::pair<uint32_t, size_t>> setup_bytes(size_t bytes)
+ std::pair<uint32_t, size_t> setup_bytes(size_t bytes)
{
- size_t requested_num_elems = bytes / sizeof(Element);
- if (std::numeric_limits<uint32_t>::max() < requested_num_elems) {
- return std::nullopt;
- }
+ uint32_t requested_num_elems(std::min<size_t>(
+ bytes / sizeof(Element),
+ std::numeric_limits<uint32_t>::max()));
- auto num_elems = setup(bytes/sizeof(Element));
+ auto num_elems = setup(requested_num_elems);
size_t approx_size_bytes = num_elems * sizeof(Element);
return std::make_pair(num_elems, approx_size_bytes);
diff --git a/src/httpserver.h b/src/httpserver.h
index 39a0a83177..33216a0119 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -131,7 +131,7 @@ public:
*/
void WriteReply(int nStatus, std::string_view reply = "")
{
- WriteReply(nStatus, std::as_bytes(std::span{reply.data(), reply.size()}));
+ WriteReply(nStatus, std::as_bytes(std::span{reply}));
}
void WriteReply(int nStatus, std::span<const std::byte> reply);
};
diff --git a/src/init.cpp b/src/init.cpp
index acee0954bf..e4b65fbfa9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -8,7 +8,6 @@
#include <init.h>
#include <kernel/checks.h>
-#include <kernel/validation_cache_sizes.h>
#include <addrman.h>
#include <banman.h>
@@ -54,7 +53,6 @@
#include <node/mempool_persist_args.h>
#include <node/miner.h>
#include <node/peerman_args.h>
-#include <node/validation_cache_args.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/fees_args.h>
@@ -119,7 +117,6 @@
using common::AmountErrMsg;
using common::InvalidPortErrMsg;
using common::ResolveErrMsg;
-using kernel::ValidationCacheSizes;
using node::ApplyArgsManOptions;
using node::BlockManager;
@@ -619,7 +616,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-test=<option>", "Pass a test-only option. Options include : " + Join(TEST_OPTIONS_DOC, ", ") + ".", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-capturemessages", "Capture all P2P messages to disk", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-mocktime=<n>", "Replace actual time with " + UNIX_EPOCH_TIME + " (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- 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);
+ argsman.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_VALIDATION_CACHE_BYTES >> 20), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-maxtipage=<n>",
strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)",
Ticks<std::chrono::seconds>(DEFAULT_MAX_TIP_AGE)),
@@ -1154,14 +1151,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
args.GetArg("-datadir", ""), fs::PathToString(fs::current_path()));
}
- ValidationCacheSizes validation_cache_sizes{};
- ApplyArgsManOptions(args, validation_cache_sizes);
- if (!InitSignatureCache(validation_cache_sizes.signature_cache_bytes)
- || !InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes))
- {
- return InitError(strprintf(_("Unable to allocate memory for -maxsigcachesize: '%s' MiB"), args.GetIntArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_BYTES >> 20)));
- }
-
assert(!node.scheduler);
node.scheduler = std::make_unique<CScheduler>();
auto& scheduler = *node.scheduler;
diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp
index bf3a340cb8..2b729e3b7a 100644
--- a/src/kernel/chainparams.cpp
+++ b/src/kernel/chainparams.cpp
@@ -495,13 +495,20 @@ public:
};
m_assumeutxo_data = {
- {
+ { // For use by unit tests
.height = 110,
.hash_serialized = AssumeutxoHash{uint256S("0x6657b736d4fe4db0cbc796789e812d5dba7f5c143764b1b6905612f1830609d1")},
.nChainTx = 111,
.blockhash = uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c")
},
{
+ // For use by fuzz target src/test/fuzz/utxo_snapshot.cpp
+ .height = 200,
+ .hash_serialized = AssumeutxoHash{uint256S("0x4f34d431c3e482f6b0d67b64609ece3964dc8d7976d02ac68dd7c9c1421738f2")},
+ .nChainTx = 201,
+ .blockhash = uint256S("0x5e93653318f294fb5aa339d00bbf8cf1c3515488ad99412c37608b139ea63b27"),
+ },
+ {
// For use by test/functional/feature_assumeutxo.py
.height = 299,
.hash_serialized = AssumeutxoHash{uint256S("0xa4bf3407ccb2cc0145c49ebba8fa91199f8a3903daf0883875941497d2493c27")},
diff --git a/src/kernel/chainstatemanager_opts.h b/src/kernel/chainstatemanager_opts.h
index 076841c3c9..1b605f3d55 100644
--- a/src/kernel/chainstatemanager_opts.h
+++ b/src/kernel/chainstatemanager_opts.h
@@ -9,6 +9,7 @@
#include <arith_uint256.h>
#include <dbwrapper.h>
+#include <script/sigcache.h>
#include <txdb.h>
#include <uint256.h>
#include <util/time.h>
@@ -48,6 +49,8 @@ struct ChainstateManagerOpts {
ValidationSignals* signals{nullptr};
//! Number of script check worker threads. Zero means no parallel verification.
int worker_threads_num{0};
+ size_t script_execution_cache_bytes{DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES};
+ size_t signature_cache_bytes{DEFAULT_SIGNATURE_CACHE_BYTES};
};
} // namespace kernel
diff --git a/src/kernel/validation_cache_sizes.h b/src/kernel/validation_cache_sizes.h
deleted file mode 100644
index 72e4d1a52c..0000000000
--- a/src/kernel/validation_cache_sizes.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2022 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_KERNEL_VALIDATION_CACHE_SIZES_H
-#define BITCOIN_KERNEL_VALIDATION_CACHE_SIZES_H
-
-#include <script/sigcache.h>
-
-#include <cstddef>
-#include <limits>
-
-namespace kernel {
-struct ValidationCacheSizes {
- size_t signature_cache_bytes{DEFAULT_MAX_SIG_CACHE_BYTES / 2};
- size_t script_execution_cache_bytes{DEFAULT_MAX_SIG_CACHE_BYTES / 2};
-};
-}
-
-#endif // BITCOIN_KERNEL_VALIDATION_CACHE_SIZES_H
diff --git a/src/net.cpp b/src/net.cpp
index 1a251d6f95..d265d78548 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -415,7 +415,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (pszDest) {
std::vector<CService> resolved{Lookup(pszDest, default_port, fNameLookup && !HaveNameProxy(), 256)};
if (!resolved.empty()) {
- Shuffle(resolved.begin(), resolved.end(), FastRandomContext());
+ std::shuffle(resolved.begin(), resolved.end(), FastRandomContext());
// If the connection is made by name, it can be the case that the name resolves to more than one address.
// We don't want to connect any more of them if we are already connected to one
for (const auto& r : resolved) {
@@ -1983,7 +1983,11 @@ bool CConnman::InactivityCheck(const CNode& node) const
}
if (!node.fSuccessfullyConnected) {
- LogPrint(BCLog::NET, "version handshake timeout peer=%d\n", node.GetId());
+ if (node.m_transport->GetInfo().transport_type == TransportProtocolType::DETECTING) {
+ LogPrint(BCLog::NET, "V2 handshake timeout peer=%d\n", node.GetId());
+ } else {
+ LogPrint(BCLog::NET, "version handshake timeout peer=%d\n", node.GetId());
+ }
return true;
}
@@ -2208,7 +2212,7 @@ void CConnman::ThreadDNSAddressSeed()
FastRandomContext rng;
std::vector<std::string> seeds = m_params.DNSSeeds();
- Shuffle(seeds.begin(), seeds.end(), rng);
+ std::shuffle(seeds.begin(), seeds.end(), rng);
int seeds_right_now = 0; // Number of seeds left before testing if we have enough connections
if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) {
@@ -2435,7 +2439,7 @@ bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
{
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
- Shuffle(nets.begin(), nets.end(), FastRandomContext());
+ std::shuffle(nets.begin(), nets.end(), FastRandomContext());
LOCK(m_nodes_mutex);
for (const auto net : nets) {
diff --git a/src/net.h b/src/net.h
index 3f0631919b..34ddd5c80d 100644
--- a/src/net.h
+++ b/src/net.h
@@ -1625,7 +1625,7 @@ private:
}
}
if (shuffle) {
- Shuffle(m_nodes_copy.begin(), m_nodes_copy.end(), FastRandomContext{});
+ std::shuffle(m_nodes_copy.begin(), m_nodes_copy.end(), FastRandomContext{});
}
}
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 8137e17c98..2764562f57 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1096,7 +1096,7 @@ private:
bool BlockRequestAllowed(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv)
- EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex);
+ EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex, !m_most_recent_block_mutex);
/**
* Validation logic for compact filters request handling.
@@ -2522,7 +2522,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
if (a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, *a_recent_compact_block);
} else {
- CBlockHeaderAndShortTxIDs cmpctblock{*pblock, FastRandomContext().rand64()};
+ CBlockHeaderAndShortTxIDs cmpctblock{*pblock, m_rng.rand64()};
MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, cmpctblock);
}
} else {
@@ -3339,7 +3339,7 @@ std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::Find1P1CPacka
// Create a random permutation of the indices.
std::vector<size_t> tx_indices(cpfp_candidates_different_peer.size());
std::iota(tx_indices.begin(), tx_indices.end(), 0);
- Shuffle(tx_indices.begin(), tx_indices.end(), m_rng);
+ std::shuffle(tx_indices.begin(), tx_indices.end(), m_rng);
for (const auto index : tx_indices) {
// If we already tried a package and failed for any reason, the combined hash was
@@ -4106,7 +4106,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const bool rate_limited = !pfrom.HasPermission(NetPermissionFlags::Addr);
uint64_t num_proc = 0;
uint64_t num_rate_limit = 0;
- Shuffle(vAddr.begin(), vAddr.end(), m_rng);
+ std::shuffle(vAddr.begin(), vAddr.end(), m_rng);
for (CAddress& addr : vAddr)
{
if (interruptMsgProc)
@@ -6244,10 +6244,13 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// before the background chainstate to prioritize getting to network tip.
FindNextBlocksToDownload(*peer, get_inflight_budget(), vToDownload, staller);
if (m_chainman.BackgroundSyncInProgress() && !IsLimitedPeer(*peer)) {
+ // If the background tip is not an ancestor of the snapshot block,
+ // we need to start requesting blocks from their last common ancestor.
+ const CBlockIndex *from_tip = LastCommonAncestor(m_chainman.GetBackgroundSyncTip(), m_chainman.GetSnapshotBaseBlock());
TryDownloadingHistoricalBlocks(
*peer,
get_inflight_budget(),
- vToDownload, m_chainman.GetBackgroundSyncTip(),
+ vToDownload, from_tip,
Assert(m_chainman.GetSnapshotBaseBlock()));
}
for (const CBlockIndex *pindex : vToDownload) {
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index fb62e78138..80aceb312a 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -594,12 +594,12 @@ bool BlockManager::IsBlockPruned(const CBlockIndex& block)
return m_have_pruned && !(block.nStatus & BLOCK_HAVE_DATA) && (block.nTx > 0);
}
-const CBlockIndex* BlockManager::GetFirstStoredBlock(const CBlockIndex& upper_block, const CBlockIndex* lower_block)
+const CBlockIndex* BlockManager::GetFirstBlock(const CBlockIndex& upper_block, uint32_t status_mask, const CBlockIndex* lower_block) const
{
AssertLockHeld(::cs_main);
const CBlockIndex* last_block = &upper_block;
- assert(last_block->nStatus & BLOCK_HAVE_DATA); // 'upper_block' must have data
- while (last_block->pprev && (last_block->pprev->nStatus & BLOCK_HAVE_DATA)) {
+ assert((last_block->nStatus & status_mask) == status_mask); // 'upper_block' must satisfy the status mask
+ while (last_block->pprev && ((last_block->pprev->nStatus & status_mask) == status_mask)) {
if (lower_block) {
// Return if we reached the lower_block
if (last_block == lower_block) return lower_block;
@@ -616,7 +616,7 @@ const CBlockIndex* BlockManager::GetFirstStoredBlock(const CBlockIndex& upper_bl
bool BlockManager::CheckBlockDataAvailability(const CBlockIndex& upper_block, const CBlockIndex& lower_block)
{
if (!(upper_block.nStatus & BLOCK_HAVE_DATA)) return false;
- return GetFirstStoredBlock(upper_block, &lower_block) == &lower_block;
+ return GetFirstBlock(upper_block, BLOCK_HAVE_DATA, &lower_block) == &lower_block;
}
// If we're using -prune with -reindex, then delete block files that will be ignored by the
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index 108a08a72b..0a46d79764 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -372,10 +372,33 @@ public:
//! (part of the same chain).
bool CheckBlockDataAvailability(const CBlockIndex& upper_block LIFETIMEBOUND, const CBlockIndex& lower_block LIFETIMEBOUND) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
- //! Find the first stored ancestor of start_block immediately after the last
- //! pruned ancestor. Return value will never be null. Caller is responsible
- //! for ensuring that start_block has data is not pruned.
- const CBlockIndex* GetFirstStoredBlock(const CBlockIndex& start_block LIFETIMEBOUND, const CBlockIndex* lower_block=nullptr) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ /**
+ * @brief Returns the earliest block with specified `status_mask` flags set after
+ * the latest block _not_ having those flags.
+ *
+ * This function starts from `upper_block`, which must have all
+ * `status_mask` flags set, and iterates backwards through its ancestors. It
+ * continues as long as each block has all `status_mask` flags set, until
+ * reaching the oldest ancestor or `lower_block`.
+ *
+ * @pre `upper_block` must have all `status_mask` flags set.
+ * @pre `lower_block` must be null or an ancestor of `upper_block`
+ *
+ * @param upper_block The starting block for the search, which must have all
+ * `status_mask` flags set.
+ * @param status_mask Bitmask specifying required status flags.
+ * @param lower_block The earliest possible block to return. If null, the
+ * search can extend to the genesis block.
+ *
+ * @return A non-null pointer to the earliest block between `upper_block`
+ * and `lower_block`, inclusive, such that every block between the
+ * returned block and `upper_block` has `status_mask` flags set.
+ */
+ const CBlockIndex* GetFirstBlock(
+ const CBlockIndex& upper_block LIFETIMEBOUND,
+ uint32_t status_mask,
+ const CBlockIndex* lower_block = nullptr
+ ) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
/** True if any block files have ever been pruned. */
bool m_have_pruned = false;
diff --git a/src/node/chainstatemanager_args.cpp b/src/node/chainstatemanager_args.cpp
index bc4a815a3e..39b5f3ad3e 100644
--- a/src/node/chainstatemanager_args.cpp
+++ b/src/node/chainstatemanager_args.cpp
@@ -56,6 +56,16 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManage
opts.worker_threads_num = std::clamp(script_threads - 1, 0, MAX_SCRIPTCHECK_THREADS);
LogPrintf("Script verification uses %d additional threads\n", opts.worker_threads_num);
+ if (auto max_size = args.GetIntArg("-maxsigcachesize")) {
+ // 1. When supplied with a max_size of 0, both the signature cache and
+ // script execution cache create the minimum possible cache (2
+ // elements). Therefore, we can use 0 as a floor here.
+ // 2. Multiply first, divide after to avoid integer truncation.
+ size_t clamped_size_each = std::max<int64_t>(*max_size, 0) * (1 << 20) / 2;
+ opts.script_execution_cache_bytes = clamped_size_each;
+ opts.signature_cache_bytes = clamped_size_each;
+ }
+
return {};
}
} // namespace node
diff --git a/src/node/validation_cache_args.cpp b/src/node/validation_cache_args.cpp
deleted file mode 100644
index ddf24f798d..0000000000
--- a/src/node/validation_cache_args.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2022 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 <node/validation_cache_args.h>
-
-#include <kernel/validation_cache_sizes.h>
-
-#include <common/args.h>
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <memory>
-#include <optional>
-
-using kernel::ValidationCacheSizes;
-
-namespace node {
-void ApplyArgsManOptions(const ArgsManager& argsman, ValidationCacheSizes& cache_sizes)
-{
- if (auto max_size = argsman.GetIntArg("-maxsigcachesize")) {
- // 1. When supplied with a max_size of 0, both InitSignatureCache and
- // InitScriptExecutionCache create the minimum possible cache (2
- // elements). Therefore, we can use 0 as a floor here.
- // 2. Multiply first, divide after to avoid integer truncation.
- size_t clamped_size_each = std::max<int64_t>(*max_size, 0) * (1 << 20) / 2;
- cache_sizes = {
- .signature_cache_bytes = clamped_size_each,
- .script_execution_cache_bytes = clamped_size_each,
- };
- }
-}
-} // namespace node
diff --git a/src/node/validation_cache_args.h b/src/node/validation_cache_args.h
deleted file mode 100644
index f447c13b49..0000000000
--- a/src/node/validation_cache_args.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2022 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_NODE_VALIDATION_CACHE_ARGS_H
-#define BITCOIN_NODE_VALIDATION_CACHE_ARGS_H
-
-class ArgsManager;
-namespace kernel {
-struct ValidationCacheSizes;
-};
-
-namespace node {
-void ApplyArgsManOptions(const ArgsManager& argsman, kernel::ValidationCacheSizes& cache_sizes);
-} // namespace node
-
-#endif // BITCOIN_NODE_VALIDATION_CACHE_ARGS_H
diff --git a/src/node/warnings.cpp b/src/node/warnings.cpp
index b99c845900..87389e472b 100644
--- a/src/node/warnings.cpp
+++ b/src/node/warnings.cpp
@@ -28,8 +28,7 @@ Warnings::Warnings()
}
bool Warnings::Set(warning_type id, bilingual_str message)
{
- LOCK(m_mutex);
- const auto& [_, inserted]{m_warnings.insert({id, std::move(message)})};
+ const auto& [_, inserted]{WITH_LOCK(m_mutex, return m_warnings.insert({id, std::move(message)}))};
if (inserted) uiInterface.NotifyAlertChanged();
return inserted;
}
diff --git a/src/psbt.h b/src/psbt.h
index 444fdeeb47..6d49864b3c 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -1177,8 +1177,13 @@ struct PartiallySignedTransaction
inputs.push_back(input);
// Make sure the non-witness utxo matches the outpoint
- if (input.non_witness_utxo && input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) {
- throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash");
+ if (input.non_witness_utxo) {
+ if (input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) {
+ throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash");
+ }
+ if (tx->vin[i].prevout.n >= input.non_witness_utxo->vout.size()) {
+ throw std::ios_base::failure("Input specifies output index that does not exist");
+ }
}
++i;
}
diff --git a/src/random.h b/src/random.h
index 8a6ef13d5e..536e697cca 100644
--- a/src/random.h
+++ b/src/random.h
@@ -458,29 +458,6 @@ inline uint256 GetRandHash() noexcept
return hash;
}
-/** More efficient than using std::shuffle on a FastRandomContext.
- *
- * This is more efficient as std::shuffle will consume entropy in groups of
- * 64 bits at the time and throw away most.
- *
- * This also works around a bug in libstdc++ std::shuffle that may cause
- * type::operator=(type&&) to be invoked on itself, which the library's
- * debug mode detects and panics on. This is a known issue, see
- * https://stackoverflow.com/questions/22915325/avoiding-self-assignment-in-stdshuffle
- */
-template <typename I, RandomNumberGenerator R>
-void Shuffle(I first, I last, R&& rng)
-{
- while (first != last) {
- size_t j = rng.randrange(last - first);
- if (j) {
- using std::swap;
- swap(*first, *(first + j));
- }
- ++first;
- }
-}
-
/* ============================= MISCELLANEOUS TEST-ONLY FUNCTIONS ============================= */
/** Check that OS randomness is available and returning the requested number
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 9b5c3ab5ff..1019c0de24 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -431,6 +431,7 @@ static RPCHelpMan getblockfrompeer()
"getblockfrompeer",
"Attempt to fetch block from a given peer.\n\n"
"We must have the header for this block, e.g. using submitheader.\n"
+ "The block will not have any undo data which can limit the usage of the block data in a context where the undo data is needed.\n"
"Subsequent calls for the same block may cause the response from the previous peer to be ignored.\n"
"Peers generally ignore requests for a stale block that they never fully verified, or one that is more than a month old.\n"
"When a peer does not respond with a block, we will disconnect.\n"
@@ -784,6 +785,32 @@ static RPCHelpMan getblock()
};
}
+//! Return height of highest block that has been pruned, or std::nullopt if no blocks have been pruned
+std::optional<int> GetPruneHeight(const BlockManager& blockman, const CChain& chain) {
+ AssertLockHeld(::cs_main);
+
+ // Search for the last block missing block data or undo data. Don't let the
+ // search consider the genesis block, because the genesis block does not
+ // have undo data, but should not be considered pruned.
+ const CBlockIndex* first_block{chain[1]};
+ const CBlockIndex* chain_tip{chain.Tip()};
+
+ // If there are no blocks after the genesis block, or no blocks at all, nothing is pruned.
+ if (!first_block || !chain_tip) return std::nullopt;
+
+ // If the chain tip is pruned, everything is pruned.
+ if (!((chain_tip->nStatus & BLOCK_HAVE_MASK) == BLOCK_HAVE_MASK)) return chain_tip->nHeight;
+
+ const auto& first_unpruned{*Assert(blockman.GetFirstBlock(*chain_tip, /*status_mask=*/BLOCK_HAVE_MASK, first_block))};
+ if (&first_unpruned == first_block) {
+ // All blocks between first_block and chain_tip have data, so nothing is pruned.
+ return std::nullopt;
+ }
+
+ // Block before the first unpruned block is the last pruned block.
+ return Assert(first_unpruned.pprev)->nHeight;
+}
+
static RPCHelpMan pruneblockchain()
{
return RPCHelpMan{"pruneblockchain", "",
@@ -836,8 +863,7 @@ static RPCHelpMan pruneblockchain()
}
PruneBlockFilesManual(active_chainstate, height);
- const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())};
- return block.nStatus & BLOCK_HAVE_DATA ? active_chainstate.m_blockman.GetFirstStoredBlock(block)->nHeight - 1 : block.nHeight;
+ return GetPruneHeight(chainman.m_blockman, active_chain).value_or(-1);
},
};
}
@@ -1297,8 +1323,8 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
obj.pushKV("pruned", chainman.m_blockman.IsPruneMode());
if (chainman.m_blockman.IsPruneMode()) {
- bool has_tip_data = tip.nStatus & BLOCK_HAVE_DATA;
- obj.pushKV("pruneheight", has_tip_data ? chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight : tip.nHeight + 1);
+ const auto prune_height{GetPruneHeight(chainman.m_blockman, active_chainstate.m_chain)};
+ obj.pushKV("pruneheight", prune_height ? prune_height.value() + 1 : 0);
const bool automatic_pruning{chainman.m_blockman.GetPruneTarget() != BlockManager::PRUNE_TARGET_MANUAL};
obj.pushKV("automatic_pruning", automatic_pruning);
@@ -2814,7 +2840,7 @@ static RPCHelpMan loadtxoutset()
{
NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node);
- fs::path path{AbsPathForConfigVal(EnsureArgsman(node), fs::u8path(request.params[0].get_str()))};
+ const fs::path path{AbsPathForConfigVal(EnsureArgsman(node), fs::u8path(self.Arg<std::string>("path")))};
FILE* file{fsbridge::fopen(path, "rb")};
AutoFile afile{file};
@@ -2833,7 +2859,7 @@ static RPCHelpMan loadtxoutset()
auto activation_result{chainman.ActivateSnapshot(afile, metadata, false)};
if (!activation_result) {
- throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf(_("Unable to load UTXO snapshot: %s\n"), util::ErrorString(activation_result)).original);
+ throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to load UTXO snapshot: %s. (%s)", util::ErrorString(activation_result).original, path.utf8string()));
}
UniValue result(UniValue::VOBJ);
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index c2021c3608..f6a7fe236c 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -21,6 +21,7 @@ class CBlockIndex;
class Chainstate;
class UniValue;
namespace node {
+class BlockManager;
struct NodeContext;
} // namespace node
@@ -57,4 +58,7 @@ UniValue CreateUTXOSnapshot(
const fs::path& path,
const fs::path& tmppath);
+//! Return height of highest block that has been pruned, or std::nullopt if no blocks have been pruned
+std::optional<int> GetPruneHeight(const node::BlockManager& blockman, const CChain& chain) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
#endif // BITCOIN_RPC_BLOCKCHAIN_H
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 75b538061d..ed9ef2c159 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1790,8 +1790,8 @@ static RPCHelpMan joinpsbts()
std::iota(output_indices.begin(), output_indices.end(), 0);
// Shuffle input and output indices lists
- Shuffle(input_indices.begin(), input_indices.end(), FastRandomContext());
- Shuffle(output_indices.begin(), output_indices.end(), FastRandomContext());
+ std::shuffle(input_indices.begin(), input_indices.end(), FastRandomContext());
+ std::shuffle(output_indices.begin(), output_indices.end(), FastRandomContext());
PartiallySignedTransaction shuffled_psbt;
shuffled_psbt.tx = CMutableTransaction();
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 7c6c282cc4..33531e6bf5 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -5,125 +5,80 @@
#include <script/sigcache.h>
-#include <common/system.h>
+#include <crypto/sha256.h>
#include <logging.h>
#include <pubkey.h>
#include <random.h>
+#include <script/interpreter.h>
+#include <span.h>
#include <uint256.h>
-#include <cuckoocache.h>
-
-#include <algorithm>
#include <mutex>
-#include <optional>
#include <shared_mutex>
#include <vector>
-namespace {
-/**
- * Valid signature cache, to avoid doing expensive ECDSA signature checking
- * twice for every transaction (once when accepted into memory pool, and
- * again when accepted into the block chain)
- */
-class CSignatureCache
+SignatureCache::SignatureCache(const size_t max_size_bytes)
{
-private:
- //! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature):
- CSHA256 m_salted_hasher_ecdsa;
- CSHA256 m_salted_hasher_schnorr;
- typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
- map_type setValid;
- std::shared_mutex cs_sigcache;
-
-public:
- CSignatureCache()
- {
- 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, and then pad with 'E' for ECDSA and
- // 'S' for Schnorr (followed by 0 bytes).
- static constexpr unsigned char PADDING_ECDSA[32] = {'E'};
- static constexpr unsigned char PADDING_SCHNORR[32] = {'S'};
- m_salted_hasher_ecdsa.Write(nonce.begin(), 32);
- m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32);
- m_salted_hasher_schnorr.Write(nonce.begin(), 32);
- m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32);
- }
-
- void
- ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const
- {
- CSHA256 hasher = m_salted_hasher_ecdsa;
- hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(vchSig.data(), vchSig.size()).Finalize(entry.begin());
- }
-
- void
- ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey) const
- {
- CSHA256 hasher = m_salted_hasher_schnorr;
- hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin());
- }
+ 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, and then pad with 'E' for ECDSA and
+ // 'S' for Schnorr (followed by 0 bytes).
+ static constexpr unsigned char PADDING_ECDSA[32] = {'E'};
+ static constexpr unsigned char PADDING_SCHNORR[32] = {'S'};
+ m_salted_hasher_ecdsa.Write(nonce.begin(), 32);
+ m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32);
+ m_salted_hasher_schnorr.Write(nonce.begin(), 32);
+ m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32);
- bool
- Get(const uint256& entry, const bool erase)
- {
- std::shared_lock<std::shared_mutex> lock(cs_sigcache);
- return setValid.contains(entry, erase);
- }
+ const auto [num_elems, approx_size_bytes] = setValid.setup_bytes(max_size_bytes);
+ LogPrintf("Using %zu MiB out of %zu MiB requested for signature cache, able to store %zu elements\n",
+ approx_size_bytes >> 20, max_size_bytes >> 20, num_elems);
+}
- void Set(const uint256& entry)
- {
- std::unique_lock<std::shared_mutex> lock(cs_sigcache);
- setValid.insert(entry);
- }
- std::optional<std::pair<uint32_t, size_t>> setup_bytes(size_t n)
- {
- return setValid.setup_bytes(n);
- }
-};
+void SignatureCache::ComputeEntryECDSA(uint256& entry, const uint256& hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const
+{
+ CSHA256 hasher = m_salted_hasher_ecdsa;
+ hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(vchSig.data(), vchSig.size()).Finalize(entry.begin());
+}
-/* In previous versions of this code, signatureCache was a local static variable
- * in CachingTransactionSignatureChecker::VerifySignature. We initialize
- * signatureCache outside of VerifySignature to avoid the atomic operation per
- * call overhead associated with local static variables even though
- * signatureCache could be made local to VerifySignature.
-*/
-static CSignatureCache signatureCache;
-} // namespace
+void SignatureCache::ComputeEntrySchnorr(uint256& entry, const uint256& hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey) const
+{
+ CSHA256 hasher = m_salted_hasher_schnorr;
+ hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin());
+}
-// To be called once in AppInitMain/BasicTestingSetup to initialize the
-// signatureCache.
-bool InitSignatureCache(size_t max_size_bytes)
+bool SignatureCache::Get(const uint256& entry, const bool erase)
{
- auto setup_results = signatureCache.setup_bytes(max_size_bytes);
- if (!setup_results) return false;
+ std::shared_lock<std::shared_mutex> lock(cs_sigcache);
+ return setValid.contains(entry, erase);
+}
- const auto [num_elems, approx_size_bytes] = *setup_results;
- LogPrintf("Using %zu MiB out of %zu MiB requested for signature cache, able to store %zu elements\n",
- approx_size_bytes >> 20, max_size_bytes >> 20, num_elems);
- return true;
+void SignatureCache::Set(const uint256& entry)
+{
+ std::unique_lock<std::shared_mutex> lock(cs_sigcache);
+ setValid.insert(entry);
}
bool CachingTransactionSignatureChecker::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
uint256 entry;
- signatureCache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey);
- if (signatureCache.Get(entry, !store))
+ m_signature_cache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey);
+ if (m_signature_cache.Get(entry, !store))
return true;
if (!TransactionSignatureChecker::VerifyECDSASignature(vchSig, pubkey, sighash))
return false;
if (store)
- signatureCache.Set(entry);
+ m_signature_cache.Set(entry);
return true;
}
bool CachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
{
uint256 entry;
- signatureCache.ComputeEntrySchnorr(entry, sighash, sig, pubkey);
- if (signatureCache.Get(entry, !store)) return true;
+ m_signature_cache.ComputeEntrySchnorr(entry, sighash, sig, pubkey);
+ if (m_signature_cache.Get(entry, !store)) return true;
if (!TransactionSignatureChecker::VerifySchnorrSignature(sig, pubkey, sighash)) return false;
- if (store) signatureCache.Set(entry);
+ if (store) m_signature_cache.Set(entry);
return true;
}
diff --git a/src/script/sigcache.h b/src/script/sigcache.h
index d33d60d5bc..76802e6a7c 100644
--- a/src/script/sigcache.h
+++ b/src/script/sigcache.h
@@ -6,32 +6,71 @@
#ifndef BITCOIN_SCRIPT_SIGCACHE_H
#define BITCOIN_SCRIPT_SIGCACHE_H
+#include <consensus/amount.h>
+#include <crypto/sha256.h>
+#include <cuckoocache.h>
#include <script/interpreter.h>
#include <span.h>
+#include <uint256.h>
#include <util/hasher.h>
-#include <optional>
+#include <cstddef>
+#include <shared_mutex>
#include <vector>
+class CPubKey;
+class CTransaction;
+class XOnlyPubKey;
+
// DoS prevention: limit cache size to 32MiB (over 1000000 entries on 64-bit
// systems). Due to how we count cache size, actual memory usage is slightly
// more (~32.25 MiB)
-static constexpr size_t DEFAULT_MAX_SIG_CACHE_BYTES{32 << 20};
+static constexpr size_t DEFAULT_VALIDATION_CACHE_BYTES{32 << 20};
+static constexpr size_t DEFAULT_SIGNATURE_CACHE_BYTES{DEFAULT_VALIDATION_CACHE_BYTES / 2};
+static constexpr size_t DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES{DEFAULT_VALIDATION_CACHE_BYTES / 2};
+static_assert(DEFAULT_VALIDATION_CACHE_BYTES == DEFAULT_SIGNATURE_CACHE_BYTES + DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES);
-class CPubKey;
+/**
+ * Valid signature cache, to avoid doing expensive ECDSA signature checking
+ * twice for every transaction (once when accepted into memory pool, and
+ * again when accepted into the block chain)
+ */
+class SignatureCache
+{
+private:
+ //! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature):
+ CSHA256 m_salted_hasher_ecdsa;
+ CSHA256 m_salted_hasher_schnorr;
+ typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
+ map_type setValid;
+ std::shared_mutex cs_sigcache;
+
+public:
+ SignatureCache(size_t max_size_bytes);
+
+ SignatureCache(const SignatureCache&) = delete;
+ SignatureCache& operator=(const SignatureCache&) = delete;
+
+ void ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const;
+
+ void ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey) const;
+
+ bool Get(const uint256& entry, const bool erase);
+
+ void Set(const uint256& entry);
+};
class CachingTransactionSignatureChecker : public TransactionSignatureChecker
{
private:
bool store;
+ SignatureCache& m_signature_cache;
public:
- CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn, MissingDataBehavior::ASSERT_FAIL), store(storeIn) {}
+ CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, SignatureCache& signature_cache, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn, MissingDataBehavior::ASSERT_FAIL), store(storeIn), m_signature_cache(signature_cache) {}
bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const override;
};
-[[nodiscard]] bool InitSignatureCache(size_t max_size_bytes);
-
#endif // BITCOIN_SCRIPT_SIGCACHE_H
diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index 9b8f419290..bc509d73ac 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -5,7 +5,9 @@
#include <boost/test/unit_test.hpp>
#include <chain.h>
+#include <node/blockstorage.h>
#include <rpc/blockchain.h>
+#include <sync.h>
#include <test/util/setup_common.h>
#include <util/string.h>
@@ -76,4 +78,36 @@ BOOST_AUTO_TEST_CASE(get_difficulty_for_very_high_target)
TestDifficulty(0x12345678, 5913134931067755359633408.0);
}
+//! Prune chain from height down to genesis block and check that
+//! GetPruneHeight returns the correct value
+static void CheckGetPruneHeight(node::BlockManager& blockman, CChain& chain, int height) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+{
+ AssertLockHeld(::cs_main);
+
+ // Emulate pruning all blocks from `height` down to the genesis block
+ // by unsetting the `BLOCK_HAVE_DATA` flag from `nStatus`
+ for (CBlockIndex* it{chain[height]}; it != nullptr && it->nHeight > 0; it = it->pprev) {
+ it->nStatus &= ~BLOCK_HAVE_DATA;
+ }
+
+ const auto prune_height{GetPruneHeight(blockman, chain)};
+ BOOST_REQUIRE(prune_height.has_value());
+ BOOST_CHECK_EQUAL(*prune_height, height);
+}
+
+BOOST_FIXTURE_TEST_CASE(get_prune_height, TestChain100Setup)
+{
+ LOCK(::cs_main);
+ auto& chain = m_node.chainman->ActiveChain();
+ auto& blockman = m_node.chainman->m_blockman;
+
+ // Fresh chain of 100 blocks without any pruned blocks, so std::nullopt should be returned
+ BOOST_CHECK(!GetPruneHeight(blockman, chain).has_value());
+
+ // Start pruning
+ CheckGetPruneHeight(blockman, chain, 1);
+ CheckGetPruneHeight(blockman, chain, 99);
+ CheckGetPruneHeight(blockman, chain, 100);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp
index 9eb7acc3ca..121f00bd25 100644
--- a/src/test/blockmanager_tests.cpp
+++ b/src/test/blockmanager_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <chain.h>
#include <chainparams.h>
#include <clientversion.h>
#include <node/blockstorage.h>
@@ -113,7 +114,7 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup)
};
// 1) Return genesis block when all blocks are available
- BOOST_CHECK_EQUAL(blockman.GetFirstStoredBlock(tip), chainman->ActiveChain()[0]);
+ BOOST_CHECK_EQUAL(blockman.GetFirstBlock(tip, BLOCK_HAVE_DATA), chainman->ActiveChain()[0]);
BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *chainman->ActiveChain()[0]));
// 2) Check lower_block when all blocks are available
@@ -127,7 +128,7 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup)
func_prune_blocks(last_pruned_block);
// 3) The last block not pruned is in-between upper-block and the genesis block
- BOOST_CHECK_EQUAL(blockman.GetFirstStoredBlock(tip), first_available_block);
+ BOOST_CHECK_EQUAL(blockman.GetFirstBlock(tip, BLOCK_HAVE_DATA), first_available_block);
BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *first_available_block));
BOOST_CHECK(!blockman.CheckBlockDataAvailability(tip, *last_pruned_block));
}
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 80652d5dd1..96283a3e15 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -73,8 +73,8 @@ auto& FuzzTargets()
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts)
{
- const auto it_ins{FuzzTargets().try_emplace(name, FuzzTarget /* temporary can be dropped after clang-16 */ {std::move(target), std::move(opts)})};
- Assert(it_ins.second);
+ const auto [it, ins]{FuzzTargets().try_emplace(name, FuzzTarget /* temporary can be dropped after Apple-Clang-16 ? */ {std::move(target), std::move(opts)})};
+ Assert(ins);
}
static std::string_view g_fuzz_target;
diff --git a/src/test/fuzz/random.cpp b/src/test/fuzz/random.cpp
index 96668734fd..6b2d42738b 100644
--- a/src/test/fuzz/random.cpp
+++ b/src/test/fuzz/random.cpp
@@ -26,6 +26,5 @@ FUZZ_TARGET(random)
(void)fast_random_context();
std::vector<int64_t> integrals = ConsumeRandomLengthIntegralVector<int64_t>(fuzzed_data_provider);
- Shuffle(integrals.begin(), integrals.end(), fast_random_context);
std::shuffle(integrals.begin(), integrals.end(), fast_random_context);
}
diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp
index 5fdbc9e106..3248ebc4af 100644
--- a/src/test/fuzz/script_sigcache.cpp
+++ b/src/test/fuzz/script_sigcache.cpp
@@ -18,12 +18,15 @@
namespace {
const BasicTestingSetup* g_setup;
+SignatureCache* g_signature_cache;
} // namespace
void initialize_script_sigcache()
{
static const auto testing_setup = MakeNoLogFileContext<>();
+ static SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
g_setup = testing_setup.get();
+ g_signature_cache = &signature_cache;
}
FUZZ_TARGET(script_sigcache, .init = initialize_script_sigcache)
@@ -36,7 +39,7 @@ FUZZ_TARGET(script_sigcache, .init = initialize_script_sigcache)
const CAmount amount = ConsumeMoney(fuzzed_data_provider);
const bool store = fuzzed_data_provider.ConsumeBool();
PrecomputedTransactionData tx_data;
- CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data};
+ CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, *g_signature_cache, tx_data};
if (fuzzed_data_provider.ConsumeBool()) {
const auto random_bytes = fuzzed_data_provider.ConsumeBytes<unsigned char>(64);
const XOnlyPubKey pub_key(ConsumeUInt256(fuzzed_data_provider));
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index 5b822b03f6..443d7241b5 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2022 The Bitcoin Core developers
+// Copyright (c) 2020-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -101,7 +101,6 @@ FUZZ_TARGET(string)
(void)TrimString(random_string_1, random_string_2);
(void)UrlDecode(random_string_1);
(void)ContainsNoNUL(random_string_1);
- (void)_(random_string_1.c_str());
try {
throw scriptnum_error{random_string_1};
} catch (const std::runtime_error&) {
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp
index fa608385d9..522c9c54ee 100644
--- a/src/test/fuzz/utxo_snapshot.cpp
+++ b/src/test/fuzz/utxo_snapshot.cpp
@@ -41,13 +41,39 @@ FUZZ_TARGET(utxo_snapshot, .init = initialize_chain)
{
AutoFile outfile{fsbridge::fopen(snapshot_path, "wb")};
- const auto file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
- outfile << Span{file_data};
+ // Metadata
+ if (fuzzed_data_provider.ConsumeBool()) {
+ std::vector<uint8_t> metadata{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
+ outfile << Span{metadata};
+ } else {
+ DataStream data_stream{};
+ auto msg_start = chainman.GetParams().MessageStart();
+ int base_blockheight{fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 2 * COINBASE_MATURITY)};
+ uint256 base_blockhash{g_chain->at(base_blockheight - 1)->GetHash()};
+ uint64_t m_coins_count{fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(1, 3 * COINBASE_MATURITY)};
+ SnapshotMetadata metadata{msg_start, base_blockhash, base_blockheight, m_coins_count};
+ outfile << metadata;
+ }
+ // Coins
+ if (fuzzed_data_provider.ConsumeBool()) {
+ std::vector<uint8_t> file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
+ outfile << Span{file_data};
+ } else {
+ int height{0};
+ for (const auto& block : *g_chain) {
+ auto coinbase{block->vtx.at(0)};
+ outfile << coinbase->GetHash();
+ WriteCompactSize(outfile, 1); // number of coins for the hash
+ WriteCompactSize(outfile, 0); // index of coin
+ outfile << Coin(coinbase->vout[0], height, /*fCoinBaseIn=*/1);
+ height++;
+ }
+ }
}
const auto ActivateFuzzedSnapshot{[&] {
AutoFile infile{fsbridge::fopen(snapshot_path, "rb")};
- auto msg_start = Params().MessageStart();
+ auto msg_start = chainman.GetParams().MessageStart();
SnapshotMetadata metadata{msg_start};
try {
infile >> metadata;
@@ -73,16 +99,20 @@ FUZZ_TARGET(utxo_snapshot, .init = initialize_chain)
Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash ==
*chainman.SnapshotBlockhash());
const auto& coinscache{chainman.ActiveChainstate().CoinsTip()};
- int64_t chain_tx{};
for (const auto& block : *g_chain) {
Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0}));
const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())};
- const auto num_tx{Assert(index)->nTx};
- Assert(num_tx == 1);
- chain_tx += num_tx;
+ Assert(index);
+ Assert(index->nTx == 0);
+ if (index->nHeight == chainman.GetSnapshotBaseHeight()) {
+ auto params{chainman.GetParams().AssumeutxoForHeight(index->nHeight)};
+ Assert(params.has_value());
+ Assert(params.value().nChainTx == index->nChainTx);
+ } else {
+ Assert(index->nChainTx == 0);
+ }
}
Assert(g_chain->size() == coinscache.GetCacheSize());
- Assert(chain_tx == chainman.ActiveTip()->nChainTx);
} else {
Assert(!chainman.SnapshotBlockhash());
Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index 7e39e9e4de..c99a4594ce 100644
--- a/src/test/miniscript_tests.cpp
+++ b/src/test/miniscript_tests.cpp
@@ -346,7 +346,7 @@ void TestSatisfy(const KeyConverter& converter, const std::string& testcase, con
auto challenges = FindChallenges(node); // Find all challenges in the generated miniscript.
std::vector<Challenge> challist(challenges.begin(), challenges.end());
for (int iter = 0; iter < 3; ++iter) {
- Shuffle(challist.begin(), challist.end(), g_insecure_rand_ctx);
+ std::shuffle(challist.begin(), challist.end(), g_insecure_rand_ctx);
Satisfier satisfier(converter.MsContext());
TestSignatureChecker checker(satisfier);
bool prev_mal_success = false, prev_nonmal_success = false;
diff --git a/src/test/net_peer_eviction_tests.cpp b/src/test/net_peer_eviction_tests.cpp
index 51d6c4384a..d9e1c2332e 100644
--- a/src/test/net_peer_eviction_tests.cpp
+++ b/src/test/net_peer_eviction_tests.cpp
@@ -31,7 +31,7 @@ bool IsProtected(int num_peers,
for (NodeEvictionCandidate& candidate : candidates) {
candidate_setup_fn(candidate);
}
- Shuffle(candidates.begin(), candidates.end(), random_context);
+ std::shuffle(candidates.begin(), candidates.end(), random_context);
const size_t size{candidates.size()};
const size_t expected{size - size / 2}; // Expect half the candidates will be protected.
@@ -572,7 +572,7 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
// Returns true if any of the node ids in node_ids are selected for eviction.
bool IsEvicted(std::vector<NodeEvictionCandidate> candidates, const std::unordered_set<NodeId>& node_ids, FastRandomContext& random_context)
{
- Shuffle(candidates.begin(), candidates.end(), random_context);
+ std::shuffle(candidates.begin(), candidates.end(), random_context);
const std::optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates));
if (!evicted_node_id) {
return false;
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index 9fa7135b77..3d8b543e64 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -206,10 +206,6 @@ BOOST_AUTO_TEST_CASE(stdrandom_test)
for (int j = 1; j <= 10; ++j) {
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
}
- Shuffle(test.begin(), test.end(), ctx);
- for (int j = 1; j <= 10; ++j) {
- BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
- }
}
}
@@ -220,7 +216,7 @@ BOOST_AUTO_TEST_CASE(shuffle_stat_test)
uint32_t counts[5 * 5 * 5 * 5 * 5] = {0};
for (int i = 0; i < 12000; ++i) {
int data[5] = {0, 1, 2, 3, 4};
- Shuffle(std::begin(data), std::end(data), ctx);
+ std::shuffle(std::begin(data), std::end(data), ctx);
int pos = data[0] + data[1] * 5 + data[2] * 25 + data[3] * 125 + data[4] * 625;
++counts[pos];
}
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index 54dcc218b9..f91203cc48 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -113,13 +113,14 @@ BOOST_AUTO_TEST_CASE(sign)
}
// All of the above should be OK, and the txTos have valid signatures
// Check to make sure signature verification fails if we use the wrong ScriptSig:
+ SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
for (int i = 0; i < 8; i++) {
PrecomputedTransactionData txdata(txTo[i]);
for (int j = 0; j < 8; j++)
{
CScript sigSave = txTo[i].vin[0].scriptSig;
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
- bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], CTransaction(txTo[i]), 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
+ bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], CTransaction(txTo[i]), signature_cache, 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
if (i == j)
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
else
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 39b53295e7..0d309469ef 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -1526,7 +1526,7 @@ static std::vector<unsigned int> AllConsensusFlags()
/** Precomputed list of all valid combinations of consensus-relevant script validation flags. */
static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
-static void AssetTest(const UniValue& test)
+static void AssetTest(const UniValue& test, SignatureCache& signature_cache)
{
BOOST_CHECK(test.isObject());
@@ -1543,7 +1543,7 @@ static void AssetTest(const UniValue& test)
CTransaction tx(mtx);
PrecomputedTransactionData txdata;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
- CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
+ CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, signature_cache, txdata);
for (const auto flags : ALL_CONSENSUS_FLAGS) {
// "final": true tests are valid for all flags. Others are only valid with flags that are
@@ -1561,7 +1561,7 @@ static void AssetTest(const UniValue& test)
CTransaction tx(mtx);
PrecomputedTransactionData txdata;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
- CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
+ CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, signature_cache, txdata);
for (const auto flags : ALL_CONSENSUS_FLAGS) {
// If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
@@ -1577,6 +1577,7 @@ BOOST_AUTO_TEST_CASE(script_assets_test)
{
// See src/test/fuzz/script_assets_test_minimizer.cpp for information on how to generate
// the script_assets_test.json file used by this test.
+ SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
const char* dir = std::getenv("DIR_UNIT_TEST_DATA");
BOOST_WARN_MESSAGE(dir != nullptr, "Variable DIR_UNIT_TEST_DATA unset, skipping script_assets_test");
@@ -1597,7 +1598,7 @@ BOOST_AUTO_TEST_CASE(script_assets_test)
BOOST_CHECK(tests.size() > 0);
for (size_t i = 0; i < tests.size(); i++) {
- AssetTest(tests[i]);
+ AssetTest(tests[i], signature_cache);
}
file.close();
}
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 34176626f0..a7fda5865c 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -17,6 +17,7 @@
#include <policy/settings.h>
#include <script/script.h>
#include <script/script_error.h>
+#include <script/sigcache.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/solver.h>
@@ -578,9 +579,11 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction)
coins.emplace_back(std::move(coin));
}
+ SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
+
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
std::vector<CScriptCheck> vChecks;
- vChecks.emplace_back(coins[tx.vin[i].prevout.n].out, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata);
+ vChecks.emplace_back(coins[tx.vin[i].prevout.n].out, tx, signature_cache, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata);
control.Add(std::move(vChecks));
}
diff --git a/src/test/txpackage_tests.cpp b/src/test/txpackage_tests.cpp
index 478121cc6f..8c873c85a3 100644
--- a/src/test/txpackage_tests.cpp
+++ b/src/test/txpackage_tests.cpp
@@ -303,7 +303,7 @@ BOOST_FIXTURE_TEST_CASE(noncontextual_package_tests, TestChain100Setup)
// The parents can be in any order.
FastRandomContext rng;
- Shuffle(package.begin(), package.end(), rng);
+ std::shuffle(package.begin(), package.end(), rng);
package.push_back(MakeTransactionRef(child));
PackageValidationState state;
diff --git a/src/test/txrequest_tests.cpp b/src/test/txrequest_tests.cpp
index dc257a0d51..0ca70d2c7a 100644
--- a/src/test/txrequest_tests.cpp
+++ b/src/test/txrequest_tests.cpp
@@ -392,7 +392,7 @@ void BuildBigPriorityTest(Scenario& scenario, int peers)
// Determine the announcement order randomly.
std::vector<NodeId> announce_order = request_order;
- Shuffle(announce_order.begin(), announce_order.end(), g_insecure_rand_ctx);
+ std::shuffle(announce_order.begin(), announce_order.end(), g_insecure_rand_ctx);
// Find a gtxid whose txhash prioritization is consistent with the required ordering within pref_peers and
// within npref_peers.
@@ -697,7 +697,7 @@ void TestInterleavedScenarios()
builders.emplace_back([](Scenario& scenario){ BuildWeirdRequestsTest(scenario); });
}
// Randomly shuffle all those functions.
- Shuffle(builders.begin(), builders.end(), g_insecure_rand_ctx);
+ std::shuffle(builders.begin(), builders.end(), g_insecure_rand_ctx);
Runner runner;
auto starttime = RandomTime1y();
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 78ef96a15d..e3b215ad83 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -5,6 +5,7 @@
#include <consensus/validation.h>
#include <key.h>
#include <random.h>
+#include <script/sigcache.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <test/util/setup_common.h>
@@ -22,6 +23,7 @@ struct Dersig100Setup : public TestChain100Setup {
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
+ ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
@@ -118,7 +120,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
// should fail.
// Capture this interaction with the upgraded_nop argument: set it when evaluating
// any script flag that is implemented as an upgraded NOP code.
-static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
PrecomputedTransactionData txdata;
@@ -140,7 +142,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
// WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH;
}
- bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, nullptr);
+ bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, nullptr);
// CheckInputScripts should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
@@ -150,13 +152,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, &scriptchecks));
BOOST_CHECK(scriptchecks.empty());
} else {
// Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
@@ -214,20 +216,20 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
TxValidationState state;
PrecomputedTransactionData ptd_spend_tx;
- BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, m_node.chainman->m_validation_cache, nullptr));
// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
- BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, m_node.chainman->m_validation_cache, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
// Test that CheckInputScripts returns true iff DERSIG-enforcing flags are
// not present. Don't add these checks to the cache, so that we can
// test later that block validation works fine in the absence of cached
// successes.
- ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
}
// And if we produce a block with this tx, it should be valid (DERSIG not
@@ -253,7 +255,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
}
// Test CHECKLOCKTIMEVERIFY
@@ -276,13 +278,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
// Make it valid, and check again
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
@@ -304,13 +306,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
vchSig.push_back((unsigned char)SIGHASH_ALL);
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
- ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
// Make it valid, and check again
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
TxValidationState state;
PrecomputedTransactionData txdata;
- BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
+ BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
}
// TODO: add tests for remaining script flags
@@ -333,11 +335,11 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
UpdateInput(valid_with_witness_tx.vin[0], sigdata);
// This should be valid under all script flags.
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
// Remove the witness, and check that it is now invalid.
valid_with_witness_tx.vin[0].scriptWitness.SetNull();
- ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
}
{
@@ -362,7 +364,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
}
// This should be valid under all script flags
- ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip());
+ ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
// Check that if the second input is invalid, but the first input is
// valid, the transaction is not cached.
@@ -372,12 +374,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
TxValidationState state;
PrecomputedTransactionData txdata;
// This transaction is now invalid under segwit, because of the second input.
- BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
+ BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first
// input was valid)
- BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
+ BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, m_node.chainman->m_validation_cache, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
}
diff --git a/src/test/util/net.cpp b/src/test/util/net.cpp
index 9b38d85d58..d1903df6ac 100644
--- a/src/test/util/net.cpp
+++ b/src/test/util/net.cpp
@@ -118,20 +118,20 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(int n_candida
candidates.reserve(n_candidates);
for (int id = 0; id < n_candidates; ++id) {
candidates.push_back({
- /*id=*/id,
- /*m_connected=*/std::chrono::seconds{random_context.randrange(100)},
- /*m_min_ping_time=*/std::chrono::microseconds{random_context.randrange(100)},
- /*m_last_block_time=*/std::chrono::seconds{random_context.randrange(100)},
- /*m_last_tx_time=*/std::chrono::seconds{random_context.randrange(100)},
- /*fRelevantServices=*/random_context.randbool(),
- /*m_relay_txs=*/random_context.randbool(),
- /*fBloomFilter=*/random_context.randbool(),
- /*nKeyedNetGroup=*/random_context.randrange(100u),
- /*prefer_evict=*/random_context.randbool(),
- /*m_is_local=*/random_context.randbool(),
- /*m_network=*/ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())],
- /*m_noban=*/false,
- /*m_conn_type=*/ConnectionType::INBOUND,
+ .id=id,
+ .m_connected=std::chrono::seconds{random_context.randrange(100)},
+ .m_min_ping_time=std::chrono::microseconds{random_context.randrange(100)},
+ .m_last_block_time=std::chrono::seconds{random_context.randrange(100)},
+ .m_last_tx_time=std::chrono::seconds{random_context.randrange(100)},
+ .fRelevantServices=random_context.randbool(),
+ .m_relay_txs=random_context.randbool(),
+ .fBloomFilter=random_context.randbool(),
+ .nKeyedNetGroup=random_context.randrange(100u),
+ .prefer_evict=random_context.randbool(),
+ .m_is_local=random_context.randbool(),
+ .m_network=ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())],
+ .m_noban=false,
+ .m_conn_type=ConnectionType::INBOUND,
});
}
return candidates;
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 52981bd2dc..5633757b97 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -6,8 +6,6 @@
#include <test/util/setup_common.h>
-#include <kernel/validation_cache_sizes.h>
-
#include <addrman.h>
#include <banman.h>
#include <chainparams.h>
@@ -30,7 +28,6 @@
#include <node/mempool_args.h>
#include <node/miner.h>
#include <node/peerman_args.h>
-#include <node/validation_cache_args.h>
#include <node/warnings.h>
#include <noui.h>
#include <policy/fees.h>
@@ -68,7 +65,6 @@
#include <stdexcept>
using kernel::BlockTreeDB;
-using kernel::ValidationCacheSizes;
using node::ApplyArgsManOptions;
using node::BlockAssembler;
using node::BlockManager;
@@ -191,11 +187,6 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
m_node.ecc_context = std::make_unique<ECC_Context>();
SetupEnvironment();
- ValidationCacheSizes validation_cache_sizes{};
- ApplyArgsManOptions(*m_node.args, validation_cache_sizes);
- Assert(InitSignatureCache(validation_cache_sizes.signature_cache_bytes));
- Assert(InitScriptExecutionCache(validation_cache_sizes.script_execution_cache_bytes));
-
m_node.chain = interfaces::MakeChain(m_node);
static bool noui_connected = false;
if (!noui_connected) {
diff --git a/src/util/translation.h b/src/util/translation.h
index d33fd2d0a0..6effe102f9 100644
--- a/src/util/translation.h
+++ b/src/util/translation.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2022 The Bitcoin Core developers
+// Copyright (c) 2019-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -67,13 +67,19 @@ bilingual_str format(const bilingual_str& fmt, const Args&... args)
/** Translate a message to the native language of the user. */
const extern std::function<std::string(const char*)> G_TRANSLATION_FUN;
+struct ConstevalStringLiteral {
+ const char* const lit;
+ consteval ConstevalStringLiteral(const char* str) : lit{str} {}
+ consteval ConstevalStringLiteral(std::nullptr_t) = delete;
+};
+
/**
* Translation function.
* If no translation function is set, simply return the input.
*/
-inline bilingual_str _(const char* psz)
+inline bilingual_str _(ConstevalStringLiteral str)
{
- return bilingual_str{psz, G_TRANSLATION_FUN ? (G_TRANSLATION_FUN)(psz) : psz};
+ return bilingual_str{str.lit, G_TRANSLATION_FUN ? (G_TRANSLATION_FUN)(str.lit) : str.lit};
}
#endif // BITCOIN_UTIL_TRANSLATION_H
diff --git a/src/validation.cpp b/src/validation.cpp
index b390a59a06..74f0e4975c 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -134,6 +134,7 @@ const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locato
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
+ ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks = nullptr)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -394,7 +395,8 @@ void Chainstate::MaybeUpdateMempoolForReorg(
* */
static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& view, const CTxMemPool& pool,
- unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip)
+ unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip,
+ ValidationCache& validation_cache)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
{
AssertLockHeld(cs_main);
@@ -426,7 +428,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
}
// Call CheckInputScripts() to cache signature and script validity against current tip consensus rules.
- return CheckInputScripts(tx, state, view, flags, /* cacheSigStore= */ true, /* cacheFullScriptStore= */ true, txdata);
+ return CheckInputScripts(tx, state, view, flags, /* cacheSigStore= */ true, /* cacheFullScriptStore= */ true, txdata, validation_cache);
}
namespace {
@@ -716,6 +718,11 @@ private:
return true;
}
+ ValidationCache& GetValidationCache()
+ {
+ return m_active_chainstate.m_chainman.m_validation_cache;
+ }
+
private:
CTxMemPool& m_pool;
CCoinsViewCache m_view;
@@ -1231,13 +1238,13 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
// Check input scripts and signatures.
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
- if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata)) {
+ if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata, GetValidationCache())) {
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
// need to turn both off, and compare against just turning off CLEANSTACK
// to see if the failure is specifically due to witness validation.
TxValidationState state_dummy; // Want reported failures to be from first CheckInputScripts
- if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, ws.m_precomputed_txdata) &&
- !CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, ws.m_precomputed_txdata)) {
+ if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, ws.m_precomputed_txdata, GetValidationCache()) &&
+ !CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, ws.m_precomputed_txdata, GetValidationCache())) {
// Only the witness is missing, so the transaction itself may be fine.
state.Invalid(TxValidationResult::TX_WITNESS_STRIPPED,
state.GetRejectReason(), state.GetDebugMessage());
@@ -1273,7 +1280,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
// transactions into the mempool can be exploited as a DoS attack.
unsigned int currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,
- ws.m_precomputed_txdata, m_active_chainstate.CoinsTip())) {
+ ws.m_precomputed_txdata, m_active_chainstate.CoinsTip(), GetValidationCache())) {
LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s\n", hash.ToString(), state.ToString());
return Assume(false);
}
@@ -2084,29 +2091,23 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
- return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error);
+ return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error);
}
-static CuckooCache::cache<uint256, SignatureCacheHasher> g_scriptExecutionCache;
-static CSHA256 g_scriptExecutionCacheHasher;
-
-bool InitScriptExecutionCache(size_t max_size_bytes)
+ValidationCache::ValidationCache(const size_t script_execution_cache_bytes, const size_t signature_cache_bytes)
+ : m_signature_cache{signature_cache_bytes}
{
// 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);
-
- auto setup_results = g_scriptExecutionCache.setup_bytes(max_size_bytes);
- if (!setup_results) return false;
+ m_script_execution_cache_hasher.Write(nonce.begin(), 32);
+ m_script_execution_cache_hasher.Write(nonce.begin(), 32);
- const auto [num_elems, approx_size_bytes] = *setup_results;
+ const auto [num_elems, approx_size_bytes] = m_script_execution_cache.setup_bytes(script_execution_cache_bytes);
LogPrintf("Using %zu MiB out of %zu MiB requested for script execution cache, able to store %zu elements\n",
- approx_size_bytes >> 20, max_size_bytes >> 20, num_elems);
- return true;
+ approx_size_bytes >> 20, script_execution_cache_bytes >> 20, num_elems);
}
/**
@@ -2131,6 +2132,7 @@ bool InitScriptExecutionCache(size_t max_size_bytes)
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
+ ValidationCache& validation_cache,
std::vector<CScriptCheck>* pvChecks)
{
if (tx.IsCoinBase()) return true;
@@ -2145,10 +2147,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
// properly commits to the scriptPubKey in the inputs view of that
// transaction).
uint256 hashCacheEntry;
- CSHA256 hasher = g_scriptExecutionCacheHasher;
+ CSHA256 hasher = validation_cache.ScriptExecutionCacheHasher();
hasher.Write(UCharCast(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 (g_scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
+ if (validation_cache.m_script_execution_cache.contains(hashCacheEntry, !cacheFullScriptStore)) {
return true;
}
@@ -2175,7 +2177,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
// spent being checked as a part of CScriptCheck.
// Verify signature
- CScriptCheck check(txdata.m_spent_outputs[i], tx, i, flags, cacheSigStore, &txdata);
+ CScriptCheck check(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i, flags, cacheSigStore, &txdata);
if (pvChecks) {
pvChecks->emplace_back(std::move(check));
} else if (!check()) {
@@ -2188,7 +2190,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
// splitting the network between upgraded and
// non-upgraded nodes by banning CONSENSUS-failing
// data providers.
- CScriptCheck check2(txdata.m_spent_outputs[i], tx, i,
+ CScriptCheck check2(txdata.m_spent_outputs[i], tx, validation_cache.m_signature_cache, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
if (check2())
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
@@ -2209,7 +2211,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
if (cacheFullScriptStore && !pvChecks) {
// We executed all of the provided scripts, and were told to
// cache the result. Do so now.
- g_scriptExecutionCache.insert(hashCacheEntry);
+ validation_cache.m_script_execution_cache.insert(hashCacheEntry);
}
return true;
@@ -2667,7 +2669,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
TxValidationState tx_state;
- if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], parallel_script_checks ? &vChecks : nullptr)) {
+ if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], m_chainman.m_validation_cache, parallel_script_checks ? &vChecks : nullptr)) {
// Any transaction validation failure in ConnectBlock is a block consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
@@ -5655,7 +5657,7 @@ util::Result<void> ChainstateManager::ActivateSnapshot(
int base_blockheight = metadata.m_base_blockheight;
if (this->SnapshotBlockhash()) {
- return util::Error{_("Can't activate a snapshot-based chainstate more than once")};
+ return util::Error{Untranslated("Can't activate a snapshot-based chainstate more than once")};
}
{
@@ -5664,7 +5666,7 @@ util::Result<void> ChainstateManager::ActivateSnapshot(
if (!GetParams().AssumeutxoForBlockhash(base_blockhash).has_value()) {
auto available_heights = GetParams().GetAvailableSnapshotHeights();
std::string heights_formatted = util::Join(available_heights, ", ", [&](const auto& i) { return util::ToString(i); });
- return util::Error{strprintf(_("assumeutxo block hash in snapshot metadata not recognized (hash: %s, height: %s). The following snapshot heights are available: %s."),
+ return util::Error{strprintf(Untranslated("assumeutxo block hash in snapshot metadata not recognized (hash: %s, height: %s). The following snapshot heights are available: %s"),
base_blockhash.ToString(),
base_blockheight,
heights_formatted)};
@@ -5672,18 +5674,18 @@ util::Result<void> ChainstateManager::ActivateSnapshot(
CBlockIndex* snapshot_start_block = m_blockman.LookupBlockIndex(base_blockhash);
if (!snapshot_start_block) {
- return util::Error{strprintf(_("The base block header (%s) must appear in the headers chain. Make sure all headers are syncing, and call loadtxoutset again."),
+ return util::Error{strprintf(Untranslated("The base block header (%s) must appear in the headers chain. Make sure all headers are syncing, and call loadtxoutset again"),
base_blockhash.ToString())};
}
bool start_block_invalid = snapshot_start_block->nStatus & BLOCK_FAILED_MASK;
if (start_block_invalid) {
- return util::Error{strprintf(_("The base block header (%s) is part of an invalid chain."), base_blockhash.ToString())};
+ return util::Error{strprintf(Untranslated("The base block header (%s) is part of an invalid chain"), base_blockhash.ToString())};
}
auto mempool{m_active_chainstate->GetMempool()};
if (mempool && mempool->size() > 0) {
- return util::Error{_("Can't activate a snapshot when mempool not empty.")};
+ return util::Error{Untranslated("Can't activate a snapshot when mempool not empty")};
}
}
@@ -5732,7 +5734,7 @@ util::Result<void> ChainstateManager::ActivateSnapshot(
static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
}
- auto cleanup_bad_snapshot = [&](const char* reason) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ auto cleanup_bad_snapshot = [&](bilingual_str&& reason) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
this->MaybeRebalanceCaches();
// PopulateAndValidateSnapshot can return (in error) before the leveldb datadir
@@ -5748,12 +5750,12 @@ util::Result<void> ChainstateManager::ActivateSnapshot(
"Manually remove it before restarting.\n"), fs::PathToString(*snapshot_datadir)));
}
}
- return util::Error{_(reason)};
+ return util::Error{std::move(reason)};
};
if (!this->PopulateAndValidateSnapshot(*snapshot_chainstate, coins_file, metadata)) {
LOCK(::cs_main);
- return cleanup_bad_snapshot("population failed");
+ return cleanup_bad_snapshot(Untranslated("population failed"));
}
LOCK(::cs_main); // cs_main required for rest of snapshot activation.
@@ -5762,13 +5764,13 @@ util::Result<void> ChainstateManager::ActivateSnapshot(
// work chain than the active chainstate; a user could have loaded a snapshot
// very late in the IBD process, and we wouldn't want to load a useless chainstate.
if (!CBlockIndexWorkComparator()(ActiveTip(), snapshot_chainstate->m_chain.Tip())) {
- return cleanup_bad_snapshot("work does not exceed active chainstate");
+ return cleanup_bad_snapshot(Untranslated("work does not exceed active chainstate"));
}
// If not in-memory, persist the base blockhash for use during subsequent
// initialization.
if (!in_memory) {
if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
- return cleanup_bad_snapshot("could not write base blockhash");
+ return cleanup_bad_snapshot(Untranslated("could not write base blockhash"));
}
}
@@ -6249,7 +6251,8 @@ ChainstateManager::ChainstateManager(const util::SignalInterrupt& interrupt, Opt
: m_script_check_queue{/*batch_size=*/128, options.worker_threads_num},
m_interrupt{interrupt},
m_options{Flatten(std::move(options))},
- m_blockman{interrupt, std::move(blockman_options)}
+ m_blockman{interrupt, std::move(blockman_options)},
+ m_validation_cache{m_options.script_execution_cache_bytes, m_options.signature_cache_bytes}
{
}
diff --git a/src/validation.h b/src/validation.h
index 2f5f2e2b95..9f99ba796b 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -10,9 +10,10 @@
#include <attributes.h>
#include <chain.h>
#include <checkqueue.h>
-#include <kernel/chain.h>
#include <consensus/amount.h>
+#include <cuckoocache.h>
#include <deploymentstatus.h>
+#include <kernel/chain.h>
#include <kernel/chainparams.h>
#include <kernel/chainstatemanager_opts.h>
#include <kernel/cs_main.h> // IWYU pragma: export
@@ -21,6 +22,7 @@
#include <policy/packages.h>
#include <policy/policy.h>
#include <script/script_error.h>
+#include <script/sigcache.h>
#include <sync.h>
#include <txdb.h>
#include <txmempool.h> // For CTxMemPool::cs
@@ -340,10 +342,11 @@ private:
bool cacheStore;
ScriptError error{SCRIPT_ERR_UNKNOWN_ERROR};
PrecomputedTransactionData *txdata;
+ SignatureCache* m_signature_cache;
public:
- CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
- m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn) { }
+ CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, SignatureCache& signature_cache, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
+ m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn), m_signature_cache(&signature_cache) { }
CScriptCheck(const CScriptCheck&) = delete;
CScriptCheck& operator=(const CScriptCheck&) = delete;
@@ -360,8 +363,28 @@ static_assert(std::is_nothrow_move_assignable_v<CScriptCheck>);
static_assert(std::is_nothrow_move_constructible_v<CScriptCheck>);
static_assert(std::is_nothrow_destructible_v<CScriptCheck>);
-/** Initializes the script-execution cache */
-[[nodiscard]] bool InitScriptExecutionCache(size_t max_size_bytes);
+/**
+ * Convenience class for initializing and passing the script execution cache
+ * and signature cache.
+ */
+class ValidationCache
+{
+private:
+ //! Pre-initialized hasher to avoid having to recreate it for every hash calculation.
+ CSHA256 m_script_execution_cache_hasher;
+
+public:
+ CuckooCache::cache<uint256, SignatureCacheHasher> m_script_execution_cache;
+ SignatureCache m_signature_cache;
+
+ ValidationCache(size_t script_execution_cache_bytes, size_t signature_cache_bytes);
+
+ ValidationCache(const ValidationCache&) = delete;
+ ValidationCache& operator=(const ValidationCache&) = delete;
+
+ //! Return a copy of the pre-initialized hasher.
+ CSHA256 ScriptExecutionCacheHasher() const { return m_script_execution_cache_hasher; }
+};
/** Functions for validating blocks and updating the block tree */
@@ -796,7 +819,6 @@ private:
friend ChainstateManager;
};
-
enum class SnapshotCompletionResult {
SUCCESS,
SKIPPED,
@@ -970,6 +992,8 @@ public:
//! chainstate to avoid duplicating block metadata.
node::BlockManager m_blockman;
+ ValidationCache m_validation_cache;
+
/**
* Whether initial block download has ended and IsInitialBlockDownload
* should return false from now on.
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index f1706b6800..edde912ce0 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -549,7 +549,7 @@ util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utx
std::vector<size_t> indexes;
indexes.resize(utxo_pool.size());
std::iota(indexes.begin(), indexes.end(), 0);
- Shuffle(indexes.begin(), indexes.end(), rng);
+ std::shuffle(indexes.begin(), indexes.end(), rng);
CAmount selected_eff_value = 0;
int weight = 0;
@@ -659,7 +659,7 @@ util::Result<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, c
std::vector<OutputGroup> applicable_groups;
CAmount nTotalLower = 0;
- Shuffle(groups.begin(), groups.end(), rng);
+ std::shuffle(groups.begin(), groups.end(), rng);
for (const OutputGroup& group : groups) {
if (group.GetSelectionAmount() == nTargetValue) {
@@ -927,7 +927,7 @@ const std::set<std::shared_ptr<COutput>>& SelectionResult::GetInputSet() const
std::vector<std::shared_ptr<COutput>> SelectionResult::GetShuffledInputVector() const
{
std::vector<std::shared_ptr<COutput>> coins(m_selected_inputs.begin(), m_selected_inputs.end());
- Shuffle(coins.begin(), coins.end(), FastRandomContext());
+ std::shuffle(coins.begin(), coins.end(), FastRandomContext());
return coins;
}
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index c64aff5fe2..e4632777cc 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -84,7 +84,7 @@ bool PermitsUncompressed(IsMineSigVersion sigversion)
return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
}
-bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyScriptPubKeyMan& keystore)
+bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyDataSPKM& keystore)
{
for (const valtype& pubkey : pubkeys) {
CKeyID keyID = CPubKey(pubkey).GetID();
@@ -102,7 +102,7 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyScriptPubKeyMan&
//! scripts or simply treat any script that has been
//! stored in the keystore as spendable
// NOLINTNEXTLINE(misc-no-recursion)
-IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion, bool recurse_scripthash=true)
+IsMineResult IsMineInner(const LegacyDataSPKM& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion, bool recurse_scripthash=true)
{
IsMineResult ret = IsMineResult::NO;
@@ -217,7 +217,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
} // namespace
-isminetype LegacyScriptPubKeyMan::IsMine(const CScript& script) const
+isminetype LegacyDataSPKM::IsMine(const CScript& script) const
{
switch (IsMineInner(*this, script, IsMineSigVersion::TOP)) {
case IsMineResult::INVALID:
@@ -231,7 +231,7 @@ isminetype LegacyScriptPubKeyMan::IsMine(const CScript& script) const
assert(false);
}
-bool LegacyScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key)
+bool LegacyDataSPKM::CheckDecryptionKey(const CKeyingMaterial& master_key)
{
{
LOCK(cs_KeyStore);
@@ -585,7 +585,7 @@ int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const
return nTimeFirstKey;
}
-std::unique_ptr<SigningProvider> LegacyScriptPubKeyMan::GetSolvingProvider(const CScript& script) const
+std::unique_ptr<SigningProvider> LegacyDataSPKM::GetSolvingProvider(const CScript& script) const
{
return std::make_unique<LegacySigningProvider>(*this);
}
@@ -721,7 +721,7 @@ void LegacyScriptPubKeyMan::UpdateTimeFirstKey(int64_t nCreateTime)
NotifyFirstKeyTimeChanged(this, nTimeFirstKey);
}
-bool LegacyScriptPubKeyMan::LoadKey(const CKey& key, const CPubKey &pubkey)
+bool LegacyDataSPKM::LoadKey(const CKey& key, const CPubKey &pubkey)
{
return AddKeyPubKeyInner(key, pubkey);
}
@@ -773,7 +773,7 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& s
return true;
}
-bool LegacyScriptPubKeyMan::LoadCScript(const CScript& redeemScript)
+bool LegacyDataSPKM::LoadCScript(const CScript& redeemScript)
{
/* A sanity check was added in pull #3843 to avoid adding redeemScripts
* that never can be redeemed. However, old wallets may still contain
@@ -788,18 +788,36 @@ bool LegacyScriptPubKeyMan::LoadCScript(const CScript& redeemScript)
return FillableSigningProvider::AddCScript(redeemScript);
}
+void LegacyDataSPKM::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta)
+{
+ LOCK(cs_KeyStore);
+ mapKeyMetadata[keyID] = meta;
+}
+
void LegacyScriptPubKeyMan::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta)
{
LOCK(cs_KeyStore);
+ LegacyDataSPKM::LoadKeyMetadata(keyID, meta);
UpdateTimeFirstKey(meta.nCreateTime);
- mapKeyMetadata[keyID] = meta;
+}
+
+void LegacyDataSPKM::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta)
+{
+ LOCK(cs_KeyStore);
+ m_script_metadata[script_id] = meta;
}
void LegacyScriptPubKeyMan::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta)
{
LOCK(cs_KeyStore);
+ LegacyDataSPKM::LoadScriptMetadata(script_id, meta);
UpdateTimeFirstKey(meta.nCreateTime);
- m_script_metadata[script_id] = meta;
+}
+
+bool LegacyDataSPKM::AddKeyPubKeyInner(const CKey& key, const CPubKey& pubkey)
+{
+ LOCK(cs_KeyStore);
+ return FillableSigningProvider::AddKeyPubKey(key, pubkey);
}
bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey)
@@ -827,7 +845,7 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pu
return true;
}
-bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid)
+bool LegacyDataSPKM::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid)
{
// Set fDecryptionThoroughlyChecked to false when the checksum is invalid
if (!checksum_valid) {
@@ -837,7 +855,7 @@ bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::
return AddCryptedKeyInner(vchPubKey, vchCryptedSecret);
}
-bool LegacyScriptPubKeyMan::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
+bool LegacyDataSPKM::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
LOCK(cs_KeyStore);
assert(mapKeys.empty());
@@ -865,13 +883,13 @@ bool LegacyScriptPubKeyMan::AddCryptedKey(const CPubKey &vchPubKey,
}
}
-bool LegacyScriptPubKeyMan::HaveWatchOnly(const CScript &dest) const
+bool LegacyDataSPKM::HaveWatchOnly(const CScript &dest) const
{
LOCK(cs_KeyStore);
return setWatchOnly.count(dest) > 0;
}
-bool LegacyScriptPubKeyMan::HaveWatchOnly() const
+bool LegacyDataSPKM::HaveWatchOnly() const
{
LOCK(cs_KeyStore);
return (!setWatchOnly.empty());
@@ -905,12 +923,12 @@ bool LegacyScriptPubKeyMan::RemoveWatchOnly(const CScript &dest)
return true;
}
-bool LegacyScriptPubKeyMan::LoadWatchOnly(const CScript &dest)
+bool LegacyDataSPKM::LoadWatchOnly(const CScript &dest)
{
return AddWatchOnlyInMem(dest);
}
-bool LegacyScriptPubKeyMan::AddWatchOnlyInMem(const CScript &dest)
+bool LegacyDataSPKM::AddWatchOnlyInMem(const CScript &dest)
{
LOCK(cs_KeyStore);
setWatchOnly.insert(dest);
@@ -954,7 +972,7 @@ bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest, int64_t nCreateTim
return AddWatchOnly(dest);
}
-void LegacyScriptPubKeyMan::LoadHDChain(const CHDChain& chain)
+void LegacyDataSPKM::LoadHDChain(const CHDChain& chain)
{
LOCK(cs_KeyStore);
m_hd_chain = chain;
@@ -975,14 +993,14 @@ void LegacyScriptPubKeyMan::AddHDChain(const CHDChain& chain)
m_hd_chain = chain;
}
-void LegacyScriptPubKeyMan::AddInactiveHDChain(const CHDChain& chain)
+void LegacyDataSPKM::AddInactiveHDChain(const CHDChain& chain)
{
LOCK(cs_KeyStore);
assert(!chain.seed_id.IsNull());
m_inactive_hd_chains[chain.seed_id] = chain;
}
-bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const
+bool LegacyDataSPKM::HaveKey(const CKeyID &address) const
{
LOCK(cs_KeyStore);
if (!m_storage.HasEncryptionKeys()) {
@@ -991,7 +1009,7 @@ bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const
return mapCryptedKeys.count(address) > 0;
}
-bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const
+bool LegacyDataSPKM::GetKey(const CKeyID &address, CKey& keyOut) const
{
LOCK(cs_KeyStore);
if (!m_storage.HasEncryptionKeys()) {
@@ -1010,7 +1028,7 @@ bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const
return false;
}
-bool LegacyScriptPubKeyMan::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const
+bool LegacyDataSPKM::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const
{
CKeyMetadata meta;
{
@@ -1030,7 +1048,7 @@ bool LegacyScriptPubKeyMan::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& inf
return true;
}
-bool LegacyScriptPubKeyMan::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const
+bool LegacyDataSPKM::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const
{
LOCK(cs_KeyStore);
WatchKeyMap::const_iterator it = mapWatchKeys.find(address);
@@ -1041,7 +1059,7 @@ bool LegacyScriptPubKeyMan::GetWatchPubKey(const CKeyID &address, CPubKey &pubke
return false;
}
-bool LegacyScriptPubKeyMan::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
+bool LegacyDataSPKM::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
{
LOCK(cs_KeyStore);
if (!m_storage.HasEncryptionKeys()) {
@@ -1160,7 +1178,7 @@ void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata&
throw std::runtime_error(std::string(__func__) + ": writing HD chain model failed");
}
-void LegacyScriptPubKeyMan::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
+void LegacyDataSPKM::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
{
LOCK(cs_KeyStore);
if (keypool.m_pre_split) {
@@ -1681,7 +1699,7 @@ std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const
return set_address;
}
-std::unordered_set<CScript, SaltedSipHasher> LegacyScriptPubKeyMan::GetScriptPubKeys() const
+std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetScriptPubKeys() const
{
LOCK(cs_KeyStore);
std::unordered_set<CScript, SaltedSipHasher> spks;
@@ -1739,7 +1757,7 @@ std::unordered_set<CScript, SaltedSipHasher> LegacyScriptPubKeyMan::GetScriptPub
return spks;
}
-std::unordered_set<CScript, SaltedSipHasher> LegacyScriptPubKeyMan::GetNotMineScriptPubKeys() const
+std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetNotMineScriptPubKeys() const
{
LOCK(cs_KeyStore);
std::unordered_set<CScript, SaltedSipHasher> spks;
@@ -1749,7 +1767,7 @@ std::unordered_set<CScript, SaltedSipHasher> LegacyScriptPubKeyMan::GetNotMineSc
return spks;
}
-std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
+std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
{
LOCK(cs_KeyStore);
if (m_storage.IsLocked()) {
@@ -1816,7 +1834,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
- auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
+ auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
desc_spk_man->AddDescriptorKey(key, key.GetPubKey());
desc_spk_man->TopUp();
auto desc_spks = desc_spk_man->GetScriptPubKeys();
@@ -1861,7 +1879,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
WalletDescriptor w_desc(std::move(desc), 0, 0, chain_counter, 0);
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
- auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
+ auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
desc_spk_man->AddDescriptorKey(master_key.key, master_key.key.GetPubKey());
desc_spk_man->TopUp();
auto desc_spks = desc_spk_man->GetScriptPubKeys();
@@ -1923,7 +1941,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
} else {
// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
- auto desc_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(m_storage, w_desc, m_keypool_size));
+ auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
for (const auto& keyid : privkeyids) {
CKey key;
if (!GetKey(keyid, key)) {
@@ -2001,7 +2019,7 @@ std::optional<MigrationData> LegacyScriptPubKeyMan::MigrateToDescriptor()
return out;
}
-bool LegacyScriptPubKeyMan::DeleteRecords()
+bool LegacyDataSPKM::DeleteRecords()
{
LOCK(cs_KeyStore);
WalletBatch batch(m_storage.GetDatabase());
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 003ca26a91..6659cbf52b 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -278,31 +278,111 @@ static const std::unordered_set<OutputType> LEGACY_OUTPUT_TYPES {
class DescriptorScriptPubKeyMan;
-class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider
+// Manages the data for a LegacyScriptPubKeyMan.
+// This is the minimum necessary to load a legacy wallet so that it can be migrated.
+class LegacyDataSPKM : public ScriptPubKeyMan, public FillableSigningProvider
{
-private:
- //! keeps track of whether Unlock has run a thorough check before
- bool fDecryptionThoroughlyChecked = true;
-
+protected:
using WatchOnlySet = std::set<CScript>;
using WatchKeyMap = std::map<CKeyID, CPubKey>;
-
- WalletBatch *encrypted_batch GUARDED_BY(cs_KeyStore) = nullptr;
-
using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
+ /* the HD chain data model (external chain counters) */
+ CHDChain m_hd_chain;
+ std::unordered_map<CKeyID, CHDChain, SaltedSipHasher> m_inactive_hd_chains;
+
+ //! keeps track of whether Unlock has run a thorough check before
+ bool fDecryptionThoroughlyChecked = true;
+
+ bool AddWatchOnlyInMem(const CScript &dest);
+ virtual bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey);
+ bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
+
+public:
+ using ScriptPubKeyMan::ScriptPubKeyMan;
+
+ // Map from Key ID to key metadata.
+ std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_KeyStore);
+
+ // Map from Script ID to key metadata (for watch-only keys).
+ std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_KeyStore);
+
+ // ScriptPubKeyMan overrides
+ bool CheckDecryptionKey(const CKeyingMaterial& master_key) override;
+ std::unordered_set<CScript, SaltedSipHasher> GetScriptPubKeys() const override;
+ std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const override;
+ uint256 GetID() const override { return uint256::ONE; }
+ // TODO: Remove IsMine when deleting LegacyScriptPubKeyMan
+ isminetype IsMine(const CScript& script) const override;
+
+ // FillableSigningProvider overrides
+ bool HaveKey(const CKeyID &address) const override;
+ bool GetKey(const CKeyID &address, CKey& keyOut) const override;
+ bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
+ bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
+
+ std::set<int64_t> setInternalKeyPool GUARDED_BY(cs_KeyStore);
+ std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_KeyStore);
+ std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_KeyStore);
+ int64_t m_max_keypool_index GUARDED_BY(cs_KeyStore) = 0;
+ std::map<CKeyID, int64_t> m_pool_key_to_index;
+
+ //! Load metadata (used by LoadWallet)
+ virtual void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata);
+ virtual void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata);
+
+ //! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
+ bool LoadWatchOnly(const CScript &dest);
+ //! Returns whether the watch-only script is in the wallet
+ bool HaveWatchOnly(const CScript &dest) const;
+ //! Returns whether there are any watch-only things in the wallet
+ bool HaveWatchOnly() const;
+ //! Adds a key to the store, without saving it to disk (used by LoadWallet)
+ bool LoadKey(const CKey& key, const CPubKey &pubkey);
+ //! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
+ bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid);
+ //! Adds a CScript to the store
+ bool LoadCScript(const CScript& redeemScript);
+ //! Load a HD chain model (used by LoadWallet)
+ void LoadHDChain(const CHDChain& chain);
+ void AddInactiveHDChain(const CHDChain& chain);
+ const CHDChain& GetHDChain() const { return m_hd_chain; }
+ //! Load a keypool entry
+ void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
+
+ //! Fetches a pubkey from mapWatchKeys if it exists there
+ bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const;
+
+ /**
+ * Retrieves scripts that were imported by bugs into the legacy spkm and are
+ * simply invalid, such as a sh(sh(pkh())) script, or not watched.
+ */
+ std::unordered_set<CScript, SaltedSipHasher> GetNotMineScriptPubKeys() const;
+
+ /** Get the DescriptorScriptPubKeyMans (with private keys) that have the same scriptPubKeys as this LegacyScriptPubKeyMan.
+ * Does not modify this ScriptPubKeyMan. */
+ std::optional<MigrationData> MigrateToDescriptor();
+ /** Delete all the records ofthis LegacyScriptPubKeyMan from disk*/
+ bool DeleteRecords();
+};
+
+// Implements the full legacy wallet behavior
+class LegacyScriptPubKeyMan : public LegacyDataSPKM
+{
+private:
+ WalletBatch *encrypted_batch GUARDED_BY(cs_KeyStore) = nullptr;
+
// By default, do not scan any block until keys/scripts are generated/imported
int64_t nTimeFirstKey GUARDED_BY(cs_KeyStore) = UNKNOWN_TIME;
//! Number of pre-generated keys/scripts (part of the look-ahead process, used to detect payments)
int64_t m_keypool_size GUARDED_BY(cs_KeyStore){DEFAULT_KEYPOOL_SIZE};
- bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey);
- bool AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
+ bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) override;
/**
* Private version of AddWatchOnly method which does not accept a
@@ -315,7 +395,6 @@ private:
*/
bool AddWatchOnly(const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
- bool AddWatchOnlyInMem(const CScript &dest);
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
@@ -330,18 +409,9 @@ private:
/** Add a KeyOriginInfo to the wallet */
bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
- /* the HD chain data model (external chain counters) */
- CHDChain m_hd_chain;
- std::unordered_map<CKeyID, CHDChain, SaltedSipHasher> m_inactive_hd_chains;
-
/* HD derive new child key (on internal or external chain) */
void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
- std::set<int64_t> setInternalKeyPool GUARDED_BY(cs_KeyStore);
- std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_KeyStore);
- std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_KeyStore);
- int64_t m_max_keypool_index GUARDED_BY(cs_KeyStore) = 0;
- std::map<CKeyID, int64_t> m_pool_key_to_index;
// Tracks keypool indexes to CKeyIDs of keys that have been taken out of the keypool but may be returned to it
std::map<int64_t, CKeyID> m_index_to_reserved_key;
@@ -378,12 +448,10 @@ private:
bool TopUpChain(WalletBatch& batch, CHDChain& chain, unsigned int size);
public:
- LegacyScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) : ScriptPubKeyMan(storage), m_keypool_size(keypool_size) {}
+ LegacyScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) : LegacyDataSPKM(storage), m_keypool_size(keypool_size) {}
util::Result<CTxDestination> GetNewDestination(const OutputType type) override;
- isminetype IsMine(const CScript& script) const override;
- bool CheckDecryptionKey(const CKeyingMaterial& master_key) override;
bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
util::Result<CTxDestination> GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) override;
@@ -417,8 +485,6 @@ public:
bool CanGetAddresses(bool internal = false) const override;
- std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const override;
-
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
@@ -427,58 +493,27 @@ public:
uint256 GetID() const override;
- // Map from Key ID to key metadata.
- std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_KeyStore);
-
- // Map from Script ID to key metadata (for watch-only keys).
- std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_KeyStore);
-
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
- //! Adds a key to the store, without saving it to disk (used by LoadWallet)
- bool LoadKey(const CKey& key, const CPubKey &pubkey);
//! Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
- //! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
- bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid);
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
- //! Adds a CScript to the store
- bool LoadCScript(const CScript& redeemScript);
//! Load metadata (used by LoadWallet)
- void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata);
- void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata);
+ void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) override;
+ void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) override;
//! Generate a new key
CPubKey GenerateNewKey(WalletBatch& batch, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
/* Set the HD chain model (chain child index counters) and writes it to the database */
void AddHDChain(const CHDChain& chain);
- //! Load a HD chain model (used by LoadWallet)
- void LoadHDChain(const CHDChain& chain);
- const CHDChain& GetHDChain() const { return m_hd_chain; }
- void AddInactiveHDChain(const CHDChain& chain);
- //! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
- bool LoadWatchOnly(const CScript &dest);
- //! Returns whether the watch-only script is in the wallet
- bool HaveWatchOnly(const CScript &dest) const;
- //! Returns whether there are any watch-only things in the wallet
- bool HaveWatchOnly() const;
//! Remove a watch only script from the keystore
bool RemoveWatchOnly(const CScript &dest);
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
- //! Fetches a pubkey from mapWatchKeys if it exists there
- bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const;
-
/* SigningProvider overrides */
- bool HaveKey(const CKeyID &address) const override;
- bool GetKey(const CKeyID &address, CKey& keyOut) const override;
- bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
bool AddCScript(const CScript& redeemScript) override;
- bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
- //! Load a keypool entry
- void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
bool NewKeyPool();
void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
@@ -527,28 +562,15 @@ public:
const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; }
std::set<CKeyID> GetKeys() const override;
- std::unordered_set<CScript, SaltedSipHasher> GetScriptPubKeys() const override;
-
- /**
- * Retrieves scripts that were imported by bugs into the legacy spkm and are
- * simply invalid, such as a sh(sh(pkh())) script, or not watched.
- */
- std::unordered_set<CScript, SaltedSipHasher> GetNotMineScriptPubKeys() const;
-
- /** Get the DescriptorScriptPubKeyMans (with private keys) that have the same scriptPubKeys as this LegacyScriptPubKeyMan.
- * Does not modify this ScriptPubKeyMan. */
- std::optional<MigrationData> MigrateToDescriptor();
- /** Delete all the records ofthis LegacyScriptPubKeyMan from disk*/
- bool DeleteRecords();
};
/** Wraps a LegacyScriptPubKeyMan so that it can be returned in a new unique_ptr. Does not provide privkeys */
class LegacySigningProvider : public SigningProvider
{
private:
- const LegacyScriptPubKeyMan& m_spk_man;
+ const LegacyDataSPKM& m_spk_man;
public:
- explicit LegacySigningProvider(const LegacyScriptPubKeyMan& spk_man) : m_spk_man(spk_man) {}
+ explicit LegacySigningProvider(const LegacyDataSPKM& spk_man) : m_spk_man(spk_man) {}
bool GetCScript(const CScriptID &scriptid, CScript& script) const override { return m_spk_man.GetCScript(scriptid, script); }
bool HaveCScript(const CScriptID &scriptid) const override { return m_spk_man.HaveCScript(scriptid); }
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 0a59353052..11a42eb469 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -226,7 +226,7 @@ void CoinsResult::Erase(const std::unordered_set<COutPoint, SaltedOutpointHasher
void CoinsResult::Shuffle(FastRandomContext& rng_fast)
{
for (auto& it : coins) {
- ::Shuffle(it.second.begin(), it.second.end(), rng_fast);
+ std::shuffle(it.second.begin(), it.second.end(), rng_fast);
}
}
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 5e3a8179a2..f2110ea3f7 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -52,7 +52,7 @@ static int TraceSqlCallback(unsigned code, void* context, void* param1, void* pa
// in the log file, only expand statements that query the database, not
// statements that update the database.
char* expanded{sqlite3_stmt_readonly(stmt) ? sqlite3_expanded_sql(stmt) : nullptr};
- LogPrintf("[%s] SQLite Statement: %s\n", db->Filename(), expanded ? expanded : sqlite3_sql(stmt));
+ LogTrace(BCLog::WALLETDB, "[%s] SQLite Statement: %s\n", db->Filename(), expanded ? expanded : sqlite3_sql(stmt));
if (expanded) sqlite3_free(expanded);
}
return SQLITE_OK;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index d569c64b43..1e98cb0771 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2929,7 +2929,7 @@ bool CWallet::EraseAddressReceiveRequest(WalletBatch& batch, const CTxDestinatio
return true;
}
-std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string)
+static util::Result<fs::path> GetWalletPath(const std::string& name)
{
// Do some checking on wallet path. It should be either a:
//
@@ -2942,15 +2942,24 @@ std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, cons
if (!(path_type == fs::file_type::not_found || path_type == fs::file_type::directory ||
(path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) ||
(path_type == fs::file_type::regular && fs::PathFromString(name).filename() == fs::PathFromString(name)))) {
- error_string = Untranslated(strprintf(
+ return util::Error{Untranslated(strprintf(
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
"database/log.?????????? files can be stored, a location where such a directory could be created, "
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
- name, fs::quoted(fs::PathToString(GetWalletDir()))));
+ name, fs::quoted(fs::PathToString(GetWalletDir()))))};
+ }
+ return wallet_path;
+}
+
+std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string)
+{
+ const auto& wallet_path = GetWalletPath(name);
+ if (!wallet_path) {
+ error_string = util::ErrorString(wallet_path);
status = DatabaseStatus::FAILED_BAD_PATH;
return nullptr;
}
- return MakeDatabase(wallet_path, options, status, error_string);
+ return MakeDatabase(*wallet_path, options, status, error_string);
}
std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
@@ -3608,6 +3617,16 @@ LegacyScriptPubKeyMan* CWallet::GetLegacyScriptPubKeyMan() const
return dynamic_cast<LegacyScriptPubKeyMan*>(it->second);
}
+LegacyDataSPKM* CWallet::GetLegacyDataSPKM() const
+{
+ if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
+ return nullptr;
+ }
+ auto it = m_internal_spk_managers.find(OutputType::LEGACY);
+ if (it == m_internal_spk_managers.end()) return nullptr;
+ return dynamic_cast<LegacyDataSPKM*>(it->second);
+}
+
LegacyScriptPubKeyMan* CWallet::GetOrCreateLegacyScriptPubKeyMan()
{
SetupLegacyScriptPubKeyMan();
@@ -3624,13 +3643,22 @@ void CWallet::AddScriptPubKeyMan(const uint256& id, std::unique_ptr<ScriptPubKey
MaybeUpdateBirthTime(spkm->GetTimeFirstKey());
}
+LegacyDataSPKM* CWallet::GetOrCreateLegacyDataSPKM()
+{
+ SetupLegacyScriptPubKeyMan();
+ return GetLegacyDataSPKM();
+}
+
void CWallet::SetupLegacyScriptPubKeyMan()
{
if (!m_internal_spk_managers.empty() || !m_external_spk_managers.empty() || !m_spk_managers.empty() || IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
return;
}
- auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new LegacyScriptPubKeyMan(*this, m_keypool_size));
+ std::unique_ptr<ScriptPubKeyMan> spk_manager = m_database->Format() == "bdb_ro" ?
+ std::make_unique<LegacyDataSPKM>(*this) :
+ std::make_unique<LegacyScriptPubKeyMan>(*this, m_keypool_size);
+
for (const auto& type : LEGACY_OUTPUT_TYPES) {
m_internal_spk_managers[type] = spk_manager.get();
m_external_spk_managers[type] = spk_manager.get();
@@ -3998,7 +4026,7 @@ std::optional<MigrationData> CWallet::GetDescriptorsForLegacy(bilingual_str& err
{
AssertLockHeld(cs_wallet);
- LegacyScriptPubKeyMan* legacy_spkm = GetLegacyScriptPubKeyMan();
+ LegacyDataSPKM* legacy_spkm = GetLegacyDataSPKM();
if (!Assume(legacy_spkm)) {
// This shouldn't happen
error = Untranslated(STR_INTERNAL_BUG("Error: Legacy wallet data missing"));
@@ -4017,7 +4045,7 @@ bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
{
AssertLockHeld(cs_wallet);
- LegacyScriptPubKeyMan* legacy_spkm = GetLegacyScriptPubKeyMan();
+ LegacyDataSPKM* legacy_spkm = GetLegacyDataSPKM();
if (!Assume(legacy_spkm)) {
// This shouldn't happen
error = Untranslated(STR_INTERNAL_BUG("Error: Legacy wallet data missing"));
@@ -4352,11 +4380,24 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
// If the wallet is still loaded, unload it so that nothing else tries to use it while we're changing it
bool was_loaded = false;
if (auto wallet = GetWallet(context, wallet_name)) {
+ if (wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
+ return util::Error{_("Error: This wallet is already a descriptor wallet")};
+ }
+
if (!RemoveWallet(context, wallet, /*load_on_start=*/std::nullopt, warnings)) {
return util::Error{_("Unable to unload the wallet before migrating")};
}
UnloadWallet(std::move(wallet));
was_loaded = true;
+ } else {
+ // Check if the wallet is BDB
+ const auto& wallet_path = GetWalletPath(wallet_name);
+ if (!wallet_path) {
+ return util::Error{util::ErrorString(wallet_path)};
+ }
+ if (!IsBDBFile(BDBDataFile(*wallet_path))) {
+ return util::Error{_("Error: This wallet is already a descriptor wallet")};
+ }
}
// Load the wallet but only in the context of this function.
@@ -4365,6 +4406,7 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
empty_context.args = context.args;
DatabaseOptions options;
options.require_existing = true;
+ options.require_format = DatabaseFormat::BERKELEY_RO;
DatabaseStatus status;
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(wallet_name, options, status, error);
if (!database) {
@@ -4379,6 +4421,8 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
// Helper to reload as normal for some of our exit scenarios
const auto& reload_wallet = [&](std::shared_ptr<CWallet>& to_reload) {
+ // Reset options.require_format as wallets of any format may be reloaded.
+ options.require_format = std::nullopt;
assert(to_reload.use_count() == 1);
std::string name = to_reload->GetName();
to_reload.reset();
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 5bc888462f..984a2d9c48 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -963,8 +963,10 @@ public:
//! Get the LegacyScriptPubKeyMan which is used for all types, internal, and external.
LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const;
LegacyScriptPubKeyMan* GetOrCreateLegacyScriptPubKeyMan();
+ LegacyDataSPKM* GetLegacyDataSPKM() const;
+ LegacyDataSPKM* GetOrCreateLegacyDataSPKM();
- //! Make a LegacyScriptPubKeyMan and set it for all types, internal, and external.
+ //! Make a Legacy(Data)SPKM and set it for all types, internal, and external.
void SetupLegacyScriptPubKeyMan();
bool WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const override;
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index f34fcfc3fd..61cc9dbc78 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -354,9 +354,9 @@ bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::stri
strErr = "Error reading wallet database: CPrivKey corrupt";
return false;
}
- if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey))
+ if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadKey(key, vchPubKey))
{
- strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadKey failed";
+ strErr = "Error reading wallet database: LegacyDataSPKM::LoadKey failed";
return false;
}
} catch (const std::exception& e) {
@@ -393,9 +393,9 @@ bool LoadCryptedKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, st
}
}
- if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid))
+ if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid))
{
- strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed";
+ strErr = "Error reading wallet database: LegacyDataSPKM::LoadCryptedKey failed";
return false;
}
} catch (const std::exception& e) {
@@ -440,7 +440,7 @@ bool LoadHDChain(CWallet* pwallet, DataStream& ssValue, std::string& strErr)
try {
CHDChain chain;
ssValue >> chain;
- pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain);
+ pwallet->GetOrCreateLegacyDataSPKM()->LoadHDChain(chain);
} catch (const std::exception& e) {
if (strErr.empty()) {
strErr = e.what();
@@ -584,9 +584,9 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
key >> hash;
CScript script;
value >> script;
- if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCScript(script))
+ if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadCScript(script))
{
- strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed";
+ strErr = "Error reading wallet database: LegacyDataSPKM::LoadCScript failed";
return DBErrors::NONCRITICAL_ERROR;
}
return DBErrors::LOAD_OK;
@@ -607,7 +607,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
key >> vchPubKey;
CKeyMetadata keyMeta;
value >> keyMeta;
- pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
+ pwallet->GetOrCreateLegacyDataSPKM()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
// Extract some CHDChain info from this metadata if it has any
if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) {
@@ -674,7 +674,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
// Set inactive chains
if (!hd_chains.empty()) {
- LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan();
+ LegacyDataSPKM* legacy_spkm = pwallet->GetLegacyDataSPKM();
if (legacy_spkm) {
for (const auto& [hd_seed_id, chain] : hd_chains) {
if (hd_seed_id != legacy_spkm->GetHDChain().seed_id) {
@@ -695,7 +695,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
uint8_t fYes;
value >> fYes;
if (fYes == '1') {
- pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script);
+ pwallet->GetOrCreateLegacyDataSPKM()->LoadWatchOnly(script);
}
return DBErrors::LOAD_OK;
});
@@ -708,7 +708,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
key >> script;
CKeyMetadata keyMeta;
value >> keyMeta;
- pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadScriptMetadata(CScriptID(script), keyMeta);
+ pwallet->GetOrCreateLegacyDataSPKM()->LoadScriptMetadata(CScriptID(script), keyMeta);
return DBErrors::LOAD_OK;
});
result = std::max(result, watch_meta_res.m_result);
@@ -720,7 +720,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
key >> nIndex;
CKeyPool keypool;
value >> keypool;
- pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool);
+ pwallet->GetOrCreateLegacyDataSPKM()->LoadKeyPool(nIndex, keypool);
return DBErrors::LOAD_OK;
});
result = std::max(result, pool_res.m_result);
@@ -763,7 +763,7 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch,
// nTimeFirstKey is only reliable if all keys have metadata
if (pwallet->IsLegacy() && (key_res.m_records + ckey_res.m_records + watch_script_res.m_records) != (keymeta_res.m_records + watch_meta_res.m_records)) {
- auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan();
+ auto spk_man = pwallet->GetLegacyScriptPubKeyMan();
if (spk_man) {
LOCK(spk_man->cs_KeyStore);
spk_man->UpdateTimeFirstKey(1);