aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bench/rollingbloom.cpp9
-rw-r--r--src/bloom.cpp5
-rw-r--r--src/httpserver.cpp8
-rw-r--r--src/init.cpp3
-rw-r--r--src/interfaces/chain.cpp26
-rw-r--r--src/net_processing.cpp21
-rw-r--r--src/qt/bitcoingui.cpp15
-rw-r--r--src/qt/forms/coincontroldialog.ui6
-rw-r--r--src/qt/test/wallettests.cpp2
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/qt/walletmodel.h11
-rw-r--r--src/random.cpp58
-rw-r--r--src/random.h2
-rw-r--r--src/rpc/misc.cpp2
-rw-r--r--src/sync.h14
-rw-r--r--src/test/versionbits_tests.cpp30
-rw-r--r--src/threadsafety.h11
-rw-r--r--src/txdb.cpp1
-rw-r--r--src/validation.cpp2
-rw-r--r--src/wallet/db.cpp4
-rw-r--r--src/wallet/rpcdump.cpp74
-rw-r--r--src/wallet/rpcwallet.cpp3
-rw-r--r--src/wallet/test/wallet_tests.cpp10
-rw-r--r--src/wallet/wallet.cpp147
-rw-r--r--src/wallet/wallet.h33
-rw-r--r--src/wallet/walletdb.h14
-rw-r--r--src/wallet/wallettool.cpp2
-rw-r--r--src/warnings.cpp8
28 files changed, 341 insertions, 182 deletions
diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp
index 0a99ea3184..4016530dac 100644
--- a/src/bench/rollingbloom.cpp
+++ b/src/bench/rollingbloom.cpp
@@ -28,4 +28,13 @@ static void RollingBloom(benchmark::State& state)
}
}
+static void RollingBloomReset(benchmark::State& state)
+{
+ CRollingBloomFilter filter(120000, 0.000001);
+ while (state.KeepRunning()) {
+ filter.reset();
+ }
+}
+
BENCHMARK(RollingBloom, 1500 * 1000);
+BENCHMARK(RollingBloomReset, 20000);
diff --git a/src/bloom.cpp b/src/bloom.cpp
index 7732cee275..a061925089 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -14,6 +14,7 @@
#include <math.h>
#include <stdlib.h>
+#include <algorithm>
#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
#define LN2 0.6931471805599453094172321214581765680755001343602552
@@ -304,7 +305,5 @@ void CRollingBloomFilter::reset()
nTweak = GetRand(std::numeric_limits<unsigned int>::max());
nEntriesThisGeneration = 0;
nGeneration = 1;
- for (std::vector<uint64_t>::iterator it = data.begin(); it != data.end(); it++) {
- *it = 0;
- }
+ std::fill(data.begin(), data.end(), 0);
}
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 63639fa3e0..d17667223b 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -139,15 +139,15 @@ struct HTTPPathHandler
//! libevent event loop
static struct event_base* eventBase = nullptr;
//! HTTP server
-struct evhttp* eventHTTP = nullptr;
+static struct evhttp* eventHTTP = nullptr;
//! List of subnets to allow RPC connections from
static std::vector<CSubNet> rpc_allow_subnets;
//! Work queue for handling longer requests off the event loop thread
static WorkQueue<HTTPClosure>* workQueue = nullptr;
//! Handlers for (sub)paths
-std::vector<HTTPPathHandler> pathHandlers;
+static std::vector<HTTPPathHandler> pathHandlers;
//! Bound listening sockets
-std::vector<evhttp_bound_socket *> boundSockets;
+static std::vector<evhttp_bound_socket *> boundSockets;
/** Check if a network address is allowed to access the HTTP server */
static bool ClientAllowed(const CNetAddr& netaddr)
@@ -420,7 +420,7 @@ bool UpdateHTTPServerLogging(bool enable) {
#endif
}
-std::thread threadHTTP;
+static std::thread threadHTTP;
static std::vector<std::thread> g_thread_http_workers;
void StartHTTPServer()
diff --git a/src/init.cpp b/src/init.cpp
index 944200e24d..2bb270647a 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -74,7 +74,7 @@
#include <zmq/zmqrpc.h>
#endif
-bool fFeeEstimatesInitialized = false;
+static bool fFeeEstimatesInitialized = false;
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
@@ -1522,6 +1522,7 @@ bool AppInitMain(InitInterfaces& interfaces)
// Note that it also sets fReindex based on the disk flag!
// From here on out fReindex and fReset mean something different!
if (!LoadBlockIndex(chainparams)) {
+ if (ShutdownRequested()) break;
strLoadError = _("Error loading block database");
break;
}
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 6097d80931..a864f21f04 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -41,7 +41,7 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
{
Optional<int> getHeight() override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
int height = ::ChainActive().Height();
if (height >= 0) {
return height;
@@ -50,7 +50,7 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
}
Optional<int> getBlockHeight(const uint256& hash) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
CBlockIndex* block = LookupBlockIndex(hash);
if (block && ::ChainActive().Contains(block)) {
return block->nHeight;
@@ -65,34 +65,34 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
}
uint256 getBlockHash(int height) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
CBlockIndex* block = ::ChainActive()[height];
assert(block != nullptr);
return block->GetBlockHash();
}
int64_t getBlockTime(int height) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
CBlockIndex* block = ::ChainActive()[height];
assert(block != nullptr);
return block->GetBlockTime();
}
int64_t getBlockMedianTimePast(int height) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
CBlockIndex* block = ::ChainActive()[height];
assert(block != nullptr);
return block->GetMedianTimePast();
}
bool haveBlockOnDisk(int height) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
CBlockIndex* block = ::ChainActive()[height];
return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
}
Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
CBlockIndex* block = ::ChainActive().FindEarliestAtLeast(time, height);
if (block) {
if (hash) *hash = block->GetBlockHash();
@@ -102,7 +102,7 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
}
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
if (::fPruneMode) {
CBlockIndex* block = stop_height ? ::ChainActive()[*stop_height] : ::ChainActive().Tip();
while (block && block->nHeight >= start_height) {
@@ -116,7 +116,7 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
}
Optional<int> findFork(const uint256& hash, Optional<int>* height) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
const CBlockIndex* block = LookupBlockIndex(hash);
const CBlockIndex* fork = block ? ::ChainActive().FindFork(block) : nullptr;
if (height) {
@@ -133,12 +133,12 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
}
CBlockLocator getTipLocator() override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
return ::ChainActive().GetLocator();
}
Optional<int> findLocatorFork(const CBlockLocator& locator) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
if (CBlockIndex* fork = FindForkInGlobalIndex(::ChainActive(), locator)) {
return fork->nHeight;
}
@@ -146,12 +146,12 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
}
bool checkFinalTx(const CTransaction& tx) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
return CheckFinalTx(tx);
}
bool submitToMemoryPool(const CTransactionRef& tx, CAmount absurd_fee, CValidationState& state) override
{
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
return AcceptToMemoryPool(::mempool, state, tx, nullptr /* missing inputs */, nullptr /* txn replaced */,
false /* bypass limits */, absurd_fee);
}
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index cb2b9f8c03..a3b865a69e 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -3288,23 +3288,22 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
if (m_enable_bip61) {
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
}
- if (strstr(e.what(), "end of data"))
- {
+ if (strstr(e.what(), "end of data")) {
// Allow exceptions from under-length message on vRecv
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "size too large"))
- {
+ } else if (strstr(e.what(), "size too large")) {
// Allow exceptions from over-long size
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
- {
+ } else if (strstr(e.what(), "non-canonical ReadCompactSize()")) {
// Allow exceptions from non-canonical encoding
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- }
- else
- {
+ } else if (strstr(e.what(), "Superfluous witness record")) {
+ // Allow exceptions from illegal witness encoding
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ } else if (strstr(e.what(), "Unknown transaction optional data")) {
+ // Allow exceptions from unknown witness encoding
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
+ } else {
PrintExceptionContinue(&e, "ProcessMessages()");
}
}
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index abf9136eee..ff66df376b 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -370,9 +370,18 @@ void BitcoinGUI::createActions()
connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked);
connect(m_open_wallet_action->menu(), &QMenu::aboutToShow, [this] {
m_open_wallet_action->menu()->clear();
- for (std::string path : m_wallet_controller->getWalletsAvailableToOpen()) {
+ std::vector<std::string> available_wallets = m_wallet_controller->getWalletsAvailableToOpen();
+ std::vector<std::string> wallets = m_node.listWalletDir();
+ for (const auto& path : wallets) {
QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
QAction* action = m_open_wallet_action->menu()->addAction(name);
+
+ if (std::find(available_wallets.begin(), available_wallets.end(), path) == available_wallets.end()) {
+ // This wallet is already loaded
+ action->setEnabled(false);
+ continue;
+ }
+
connect(action, &QAction::triggered, [this, name, path] {
OpenWalletActivity* activity = m_wallet_controller->openWallet(path);
@@ -400,6 +409,10 @@ void BitcoinGUI::createActions()
assert(invoked);
});
}
+ if (wallets.empty()) {
+ QAction* action = m_open_wallet_action->menu()->addAction(tr("No wallets available"));
+ action->setEnabled(false);
+ }
});
connect(m_close_wallet_action, &QAction::triggered, [this] {
m_wallet_controller->closeWallet(walletFrame->currentWalletModel(), this);
diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui
index bd7f3c5f56..5ce469ee96 100644
--- a/src/qt/forms/coincontroldialog.ui
+++ b/src/qt/forms/coincontroldialog.ui
@@ -467,12 +467,6 @@
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 21209d4994..ab40e9962b 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -145,7 +145,7 @@ void TestGUI()
}
{
auto locked_chain = wallet->chain().lock();
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index fd392b7cf7..a2b295df21 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -482,7 +482,7 @@ WalletModel::UnlockContext::~UnlockContext()
}
}
-void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs)
+void WalletModel::UnlockContext::CopyFrom(UnlockContext&& rhs)
{
// Transfer context; old object no longer relocks wallet
*this = rhs;
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index b123befbb4..54428aec08 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -194,15 +194,18 @@ public:
bool isValid() const { return valid; }
- // Copy operator and constructor transfer the context
- UnlockContext(const UnlockContext& obj) { CopyFrom(obj); }
- UnlockContext& operator=(const UnlockContext& rhs) { CopyFrom(rhs); return *this; }
+ // Copy constructor is disabled.
+ UnlockContext(const UnlockContext&) = delete;
+ // Move operator and constructor transfer the context
+ UnlockContext(UnlockContext&& obj) { CopyFrom(std::move(obj)); }
+ UnlockContext& operator=(UnlockContext&& rhs) { CopyFrom(std::move(rhs)); return *this; }
private:
WalletModel *wallet;
bool valid;
mutable bool relock; // mutable, as it can be set to false by copying
- void CopyFrom(const UnlockContext& rhs);
+ UnlockContext& operator=(const UnlockContext&) = default;
+ void CopyFrom(UnlockContext&& rhs);
};
UnlockContext requestUnlock();
diff --git a/src/random.cpp b/src/random.cpp
index b08de60fbe..de26e6de1a 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -237,6 +237,34 @@ static void SeedHardwareSlow(CSHA512& hasher) noexcept {
#endif
}
+/** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */
+static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA512& hasher) noexcept
+{
+ CSHA512 inner_hasher;
+ inner_hasher.Write(seed, sizeof(seed));
+
+ // Hash loop
+ unsigned char buffer[64];
+ int64_t stop = GetTimeMicros() + microseconds;
+ do {
+ for (int i = 0; i < 1000; ++i) {
+ inner_hasher.Finalize(buffer);
+ inner_hasher.Reset();
+ inner_hasher.Write(buffer, sizeof(buffer));
+ }
+ // Benchmark operation and feed it into outer hasher.
+ int64_t perf = GetPerformanceCounter();
+ hasher.Write((const unsigned char*)&perf, sizeof(perf));
+ } while (GetTimeMicros() < stop);
+
+ // Produce output from inner state and feed it to outer hasher.
+ inner_hasher.Finalize(buffer);
+ hasher.Write(buffer, sizeof(buffer));
+ // Try to clean up.
+ inner_hasher.Reset();
+ memory_cleanse(buffer, sizeof(buffer));
+}
+
static void RandAddSeedPerfmon(CSHA512& hasher)
{
#ifdef WIN32
@@ -529,7 +557,23 @@ static void SeedSlow(CSHA512& hasher) noexcept
SeedTimestamp(hasher);
}
-static void SeedSleep(CSHA512& hasher)
+/** Extract entropy from rng, strengthen it, and feed it into hasher. */
+static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept
+{
+ static std::atomic<int64_t> last_strengthen{0};
+ int64_t last_time = last_strengthen.load();
+ int64_t current_time = GetTimeMicros();
+ if (current_time > last_time + 60000000) { // Only run once a minute
+ // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
+ unsigned char strengthen_seed[32];
+ rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
+ // Strengthen it for 10ms (100ms on first run), and feed it into hasher.
+ Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher);
+ last_strengthen = current_time;
+ }
+}
+
+static void SeedSleep(CSHA512& hasher, RNGState& rng)
{
// Everything that the 'fast' seeder includes
SeedFast(hasher);
@@ -545,9 +589,12 @@ static void SeedSleep(CSHA512& hasher)
// Windows performance monitor data (once every 10 minutes)
RandAddSeedPerfmon(hasher);
+
+ // Strengthen every minute
+ SeedStrengthen(hasher, rng);
}
-static void SeedStartup(CSHA512& hasher) noexcept
+static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
{
#ifdef WIN32
RAND_screen();
@@ -561,6 +608,9 @@ static void SeedStartup(CSHA512& hasher) noexcept
// Windows performance monitor data.
RandAddSeedPerfmon(hasher);
+
+ // Strengthen
+ SeedStrengthen(hasher, rng);
}
enum class RNGLevel {
@@ -585,7 +635,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
SeedSlow(hasher);
break;
case RNGLevel::SLEEP:
- SeedSleep(hasher);
+ SeedSleep(hasher, rng);
break;
}
@@ -593,7 +643,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
if (!rng.MixExtract(out, num, std::move(hasher), false)) {
// On the first invocation, also seed with SeedStartup().
CSHA512 startup_hasher;
- SeedStartup(startup_hasher);
+ SeedStartup(startup_hasher, rng);
rng.MixExtract(out, num, std::move(startup_hasher), true);
}
diff --git a/src/random.h b/src/random.h
index 2f9c0f5a36..75d037738d 100644
--- a/src/random.h
+++ b/src/random.h
@@ -43,6 +43,7 @@
* - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally:
* - A high-precision timestamp before and after sleeping 1ms.
* - (On Windows) Once every 10 minutes, performance monitoring data from the OS.
+ - - Once every minute, strengthen the entropy for 10 ms using repeated SHA512.
* These just exploit the fact the system is idle to improve the quality of the RNG
* slightly.
*
@@ -51,6 +52,7 @@
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
* - (On Windows) Performance monitoring data from the OS.
* - (On Windows) Through OpenSSL, the screen contents.
+ * - Strengthen the entropy for 100 ms using repeated SHA512.
*
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
* (up to) the first 32 bytes of H are produced as output, while the last 32 bytes
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 7008a83143..72a2919712 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -204,7 +204,7 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
},
RPCExamples{
"First three native segwit receive addresses\n" +
- HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#trd0mf0l\" \"[0,2]\"")
+ HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu\" \"[0,2]\"")
}}.ToString()
);
}
diff --git a/src/sync.h b/src/sync.h
index 2667fb52f9..bdbdde1a2a 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -304,4 +304,18 @@ public:
}
};
+// Utility class for indicating to compiler thread analysis that a mutex is
+// locked (when it couldn't be determined otherwise).
+struct SCOPED_LOCKABLE LockAssertion
+{
+ template <typename Mutex>
+ explicit LockAssertion(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
+ {
+#ifdef DEBUG_LOCKORDER
+ AssertLockHeld(mutex);
+#endif
+ }
+ ~LockAssertion() UNLOCK_FUNCTION() {}
+};
+
#endif // BITCOIN_SYNC_H
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 38d91b6647..0ca3a17974 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -271,12 +271,12 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Before MedianTimePast of the chain has crossed nStartTime, the bit
// should not be set.
CBlockIndex *lastBlock = nullptr;
- lastBlock = firstChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
- // Mine 2011 more blocks at the old time, and check that CBV isn't setting the bit yet.
- for (int i=1; i<2012; i++) {
- lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ // Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
+ for (uint32_t i = 1; i < mainnetParams.nMinerConfirmationWindow - 4; i++) {
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens
// to be 4, and the bit we're testing happens to be bit 28.
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
@@ -284,13 +284,13 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
// CBV should still not yet set the bit.
nTime = nStartTime;
- for (int i=2012; i<=2016; i++) {
- lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ for (uint32_t i = mainnetParams.nMinerConfirmationWindow - 4; i <= mainnetParams.nMinerConfirmationWindow; i++) {
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
}
// Advance to the next period and transition to STARTED,
- lastBlock = firstChain.Mine(6048, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// so ComputeBlockVersion should now set the bit,
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// and should also be using the VERSIONBITS_TOP_BITS.
@@ -298,8 +298,8 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Check that ComputeBlockVersion will set the bit until nTimeout
nTime += 600;
- int blocksToMine = 4032; // test blocks for up to 2 time periods
- int nHeight = 6048;
+ uint32_t blocksToMine = mainnetParams.nMinerConfirmationWindow * 2; // test blocks for up to 2 time periods
+ uint32_t nHeight = mainnetParams.nMinerConfirmationWindow * 3;
// These blocks are all before nTimeout is reached.
while (nTime < nTimeout && blocksToMine > 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
@@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
nTime = nTimeout;
// FAILED is only triggered at the end of a period, so CBV should be setting
// the bit until the period transition.
- for (int i=0; i<2015; i++) {
+ for (uint32_t i = 0; i < mainnetParams.nMinerConfirmationWindow - 1; i++) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
nHeight += 1;
@@ -329,20 +329,20 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// Mine one period worth of blocks, and check that the bit will be on for the
// next period.
- lastBlock = secondChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// Mine another period worth of blocks, signaling the new bit.
- lastBlock = secondChain.Mine(4032, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
+ lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow * 2, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
// After one period of setting the bit on each block, it should have locked in.
// We keep setting the bit for one more period though, until activation.
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// Now check that we keep mining the block until the end of this period, and
// then stop at the beginning of the next period.
- lastBlock = secondChain.Mine(6047, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
- BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
- lastBlock = secondChain.Mine(6048, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ lastBlock = secondChain.Mine((mainnetParams.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
+ BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != 0);
+ lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
// Finally, verify that after a soft fork has activated, CBV no longer uses
diff --git a/src/threadsafety.h b/src/threadsafety.h
index 33acddc65c..47e6b2ea38 100644
--- a/src/threadsafety.h
+++ b/src/threadsafety.h
@@ -54,15 +54,4 @@
#define ASSERT_EXCLUSIVE_LOCK(...)
#endif // __GNUC__
-// Utility class for indicating to compiler thread analysis that a mutex is
-// locked (when it couldn't be determined otherwise).
-struct SCOPED_LOCKABLE LockAnnotation
-{
- template <typename Mutex>
- explicit LockAnnotation(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
- {
- }
- ~LockAnnotation() UNLOCK_FUNCTION() {}
-};
-
#endif // BITCOIN_THREADSAFETY_H
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 8447352c54..494b87ad48 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -255,6 +255,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
// Load mapBlockIndex
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
+ if (ShutdownRequested()) return false;
std::pair<char, uint256> key;
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
CDiskBlockIndex diskindex;
diff --git a/src/validation.cpp b/src/validation.cpp
index b2d84f4c95..436c62261b 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -3861,6 +3861,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
sort(vSortedByHeight.begin(), vSortedByHeight.end());
for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight)
{
+ if (ShutdownRequested()) return false;
CBlockIndex* pindex = item.second;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
@@ -4306,6 +4307,7 @@ bool RewindBlockIndex(const CChainParams& params) {
return false;
}
+ LOCK(cs_main);
if (::ChainActive().Tip() != nullptr) {
// FlushStateToDisk can possibly read ::ChainActive(). Be conservative
// and skip it here, we're about to -reindex-chainstate anyway, so
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 546e3d54eb..8633d8701b 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -607,7 +607,9 @@ void BerkeleyBatch::Flush()
if (fReadOnly)
nMinutes = 1;
- env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
+ env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ }
}
void BerkeleyDatabase::IncrementUpdateCounter()
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 3861b5524f..ee1b792f9b 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -116,7 +116,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
"\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one private key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
- "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n",
+ "may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
{"label", RPCArg::Type::STR, /* default */ "current label if address exists, otherwise \"\"", "An optional label"},
@@ -220,7 +221,8 @@ UniValue abortrescan(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
RPCHelpMan{"abortrescan",
- "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n",
+ "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{},
RPCResults{},
RPCExamples{
@@ -289,8 +291,10 @@ UniValue importaddress(const JSONRPCRequest& request)
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"If you have the full public key, you should call importpubkey instead of this.\n"
+ "Hint: use importmulti to import more than one address.\n"
"\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
- "as change, and not show up in many RPCs.\n",
+ "as change, and not show up in many RPCs.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin address (or hex-encoded script)"},
{"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
@@ -486,8 +490,10 @@ UniValue importpubkey(const JSONRPCRequest& request)
throw std::runtime_error(
RPCHelpMan{"importpubkey",
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
+ "Hint: use importmulti to import more than one public key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
- "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n",
+ "may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
{"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
@@ -568,7 +574,8 @@ UniValue importwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"importwallet",
- "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n",
+ "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet file"},
},
@@ -1266,55 +1273,17 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
// All good, time to import
pwallet->MarkDirty();
- for (const auto& entry : import_data.import_scripts) {
- if (!pwallet->HaveCScript(CScriptID(entry)) && !pwallet->AddCScript(entry)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
- }
- }
- for (const auto& entry : privkey_map) {
- const CKey& key = entry.second;
- CPubKey pubkey = key.GetPubKey();
- const CKeyID& id = entry.first;
- assert(key.VerifyPubKey(pubkey));
- pwallet->mapKeyMetadata[id].nCreateTime = timestamp;
- // If the private key is not present in the wallet, insert it.
- if (!pwallet->HaveKey(id) && !pwallet->AddKeyPubKey(key, pubkey)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
- }
- pwallet->UpdateTimeFirstKey(timestamp);
+ if (!pwallet->ImportScripts(import_data.import_scripts)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
}
- for (const auto& entry : import_data.key_origins) {
- pwallet->AddKeyOrigin(entry.second.first, entry.second.second);
+ if (!pwallet->ImportPrivKeys(privkey_map, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
- for (const CKeyID& id : ordered_pubkeys) {
- auto entry = pubkey_map.find(id);
- if (entry == pubkey_map.end()) {
- continue;
- }
- const CPubKey& pubkey = entry->second;
- CPubKey temp;
- if (!pwallet->GetPubKey(id, temp) && !pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
- pwallet->mapKeyMetadata[id].nCreateTime = timestamp;
-
- // Add to keypool only works with pubkeys
- if (add_keypool) {
- pwallet->AddKeypoolPubkey(pubkey, internal);
- }
+ if (!pwallet->ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
-
- for (const CScript& script : script_pub_keys) {
- if (!have_solving_data || !::IsMine(*pwallet, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
- if (!pwallet->AddWatchOnly(script, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
- }
- CTxDestination dest;
- ExtractDestination(script, dest);
- if (!internal && IsValidDestination(dest)) {
- pwallet->SetAddressBook(dest, label, "receive");
- }
+ if (!pwallet->ImportScriptPubKeys(label, script_pub_keys, have_solving_data, internal, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
result.pushKV("success", UniValue(true));
@@ -1359,7 +1328,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
"Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
- "may report that the imported keys, addresses or scripts exists but related transactions are still missing.\n",
+ "may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
{
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 96a74a338a..71d62a47dc 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3452,7 +3452,8 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
RPCHelpMan{"rescanblockchain",
- "\nRescan the local blockchain for wallet related transactions.\n",
+ "\nRescan the local blockchain for wallet related transactions.\n"
+ "Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
{"start_height", RPCArg::Type::NUM, /* default */ "0", "block height where the rescan should start"},
{"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 62630c011a..922bb0fe65 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -44,7 +44,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
auto chain = interfaces::MakeChain();
auto locked_chain = chain->lock();
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
// Verify ScanForWalletTransactions accommodates a null start block.
{
@@ -123,7 +123,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
auto chain = interfaces::MakeChain();
auto locked_chain = chain->lock();
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
// Prune the older block file.
PruneOneBlockFile(oldTip->GetBlockPos().nFile);
@@ -190,7 +190,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
auto chain = interfaces::MakeChain();
auto locked_chain = chain->lock();
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string();
@@ -248,7 +248,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
CWalletTx wtx(&wallet, m_coinbase_txns.back());
auto locked_chain = chain->lock();
- LockAnnotation lock(::cs_main);
+ LockAssertion lock(::cs_main);
LOCK(wallet.cs_wallet);
wtx.hashBlock = ::ChainActive().Tip()->GetBlockHash();
@@ -272,8 +272,8 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
SetMockTime(mockTime);
CBlockIndex* block = nullptr;
if (blockTime > 0) {
- LockAnnotation lock(::cs_main);
auto locked_chain = wallet.chain().lock();
+ LockAssertion lock(::cs_main);
auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256& hash = inserted.first->first;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 260617c666..e86147b5d0 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -320,7 +320,7 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const C
secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
}
- UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
return true;
}
@@ -362,12 +362,6 @@ void CWallet::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata&
m_script_metadata[script_id] = meta;
}
-// Writes a keymetadata for a public key. overwrite specifies whether to overwrite an existing metadata for that key if there exists one.
-bool CWallet::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite)
-{
- return WalletBatch(*database).WriteKeyMetadata(meta, pubkey, overwrite);
-}
-
void CWallet::UpgradeKeyMetadata()
{
AssertLockHeld(cs_wallet);
@@ -376,7 +370,6 @@ void CWallet::UpgradeKeyMetadata()
}
std::unique_ptr<WalletBatch> batch = MakeUnique<WalletBatch>(*database);
- size_t cnt = 0;
for (auto& meta_pair : mapKeyMetadata) {
CKeyMetadata& meta = meta_pair.second;
if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && meta.hdKeypath != "s") { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin
@@ -399,10 +392,6 @@ void CWallet::UpgradeKeyMetadata()
CPubKey pubkey;
if (GetPubKey(meta_pair.first, pubkey)) {
batch->WriteKeyMetadata(meta, pubkey, true);
- if (++cnt % 1000 == 0) {
- // avoid creating overlarge in-memory batches in case the wallet contains large amounts of keys
- batch.reset(new WalletBatch(*database));
- }
}
}
}
@@ -433,10 +422,16 @@ void CWallet::UpdateTimeFirstKey(int64_t nCreateTime)
bool CWallet::AddCScript(const CScript& redeemScript)
{
+ WalletBatch batch(*database);
+ return AddCScriptWithDB(batch, redeemScript);
+}
+
+bool CWallet::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript)
+{
if (!CCryptoKeyStore::AddCScript(redeemScript))
return false;
- if (WalletBatch(*database).WriteCScript(Hash160(redeemScript), redeemScript)) {
- UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
+ if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) {
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
return true;
}
return false;
@@ -457,20 +452,32 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
return CCryptoKeyStore::AddCScript(redeemScript);
}
-bool CWallet::AddWatchOnly(const CScript& dest)
+bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest)
{
if (!CCryptoKeyStore::AddWatchOnly(dest))
return false;
const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime);
NotifyWatchonlyChanged(true);
- if (WalletBatch(*database).WriteWatchOnly(dest, meta)) {
- UnsetWalletFlag(WALLET_FLAG_BLANK_WALLET);
+ if (batch.WriteWatchOnly(dest, meta)) {
+ UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
return true;
}
return false;
}
+bool CWallet::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time)
+{
+ m_script_metadata[CScriptID(dest)].nCreateTime = create_time;
+ return AddWatchOnlyWithDB(batch, dest);
+}
+
+bool CWallet::AddWatchOnly(const CScript& dest)
+{
+ WalletBatch batch(*database);
+ return AddWatchOnlyWithDB(batch, dest);
+}
+
bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)
{
m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime;
@@ -1543,9 +1550,15 @@ void CWallet::SetWalletFlag(uint64_t flags)
void CWallet::UnsetWalletFlag(uint64_t flag)
{
+ WalletBatch batch(*database);
+ UnsetWalletFlagWithDB(batch, flag);
+}
+
+void CWallet::UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag)
+{
LOCK(cs_wallet);
m_wallet_flags &= ~flag;
- if (!WalletBatch(*database).WriteWalletFlags(m_wallet_flags))
+ if (!batch.WriteWalletFlags(m_wallet_flags))
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
}
@@ -1606,6 +1619,80 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut>
return true;
}
+bool CWallet::ImportScripts(const std::set<CScript> scripts)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : scripts) {
+ if (!HaveCScript(CScriptID(entry)) && !AddCScriptWithDB(batch, entry)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : privkey_map) {
+ const CKey& key = entry.second;
+ CPubKey pubkey = key.GetPubKey();
+ const CKeyID& id = entry.first;
+ assert(key.VerifyPubKey(pubkey));
+ mapKeyMetadata[id].nCreateTime = timestamp;
+ // If the private key is not present in the wallet, insert it.
+ if (!HaveKey(id) && !AddKeyPubKeyWithDB(batch, key, pubkey)) {
+ return false;
+ }
+ UpdateTimeFirstKey(timestamp);
+ }
+ return true;
+}
+
+bool CWallet::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const auto& entry : key_origins) {
+ AddKeyOriginWithDB(batch, entry.second.first, entry.second.second);
+ }
+ for (const CKeyID& id : ordered_pubkeys) {
+ auto entry = pubkey_map.find(id);
+ if (entry == pubkey_map.end()) {
+ continue;
+ }
+ const CPubKey& pubkey = entry->second;
+ CPubKey temp;
+ if (!GetPubKey(id, temp) && !AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), timestamp)) {
+ return false;
+ }
+ mapKeyMetadata[id].nCreateTime = timestamp;
+
+ // Add to keypool only works with pubkeys
+ if (add_keypool) {
+ AddKeypoolPubkeyWithDB(pubkey, internal, batch);
+ NotifyCanGetAddressesChanged();
+ }
+ }
+ return true;
+}
+
+bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool internal, const int64_t timestamp)
+{
+ WalletBatch batch(*database);
+ for (const CScript& script : script_pub_keys) {
+ if (!have_solving_data || !::IsMine(*this, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
+ if (!AddWatchOnlyWithDB(batch, script, timestamp)) {
+ return false;
+ }
+ }
+ CTxDestination dest;
+ ExtractDestination(script, dest);
+ if (!internal && IsValidDestination(dest)) {
+ SetAddressBookWithDB(batch, dest, label, "receive");
+ }
+ }
+ return true;
+}
+
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
{
std::vector<CTxOut> txouts;
@@ -3149,8 +3236,7 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
return DBErrors::LOAD_OK;
}
-
-bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
+bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
{
bool fUpdated = false;
{
@@ -3163,9 +3249,15 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
}
NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
- if (!strPurpose.empty() && !WalletBatch(*database).WritePurpose(EncodeDestination(address), strPurpose))
+ if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false;
- return WalletBatch(*database).WriteName(EncodeDestination(address), strName);
+ return batch.WriteName(EncodeDestination(address), strName);
+}
+
+bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
+{
+ WalletBatch batch(*database);
+ return SetAddressBookWithDB(batch, address, strName, strPurpose);
}
bool CWallet::DelAddressBook(const CTxDestination& address)
@@ -3315,13 +3407,6 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
return true;
}
-void CWallet::AddKeypoolPubkey(const CPubKey& pubkey, const bool internal)
-{
- WalletBatch batch(*database);
- AddKeypoolPubkeyWithDB(pubkey, internal, batch);
- NotifyCanGetAddressesChanged();
-}
-
void CWallet::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch)
{
LOCK(cs_wallet);
@@ -4443,12 +4528,12 @@ bool CWallet::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const
return true;
}
-bool CWallet::AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info)
+bool CWallet::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info)
{
LOCK(cs_wallet);
std::copy(info.fingerprint, info.fingerprint + 4, mapKeyMetadata[pubkey.GetID()].key_origin.fingerprint);
mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path;
mapKeyMetadata[pubkey.GetID()].has_key_origin = true;
mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path);
- return WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
+ return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true);
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 5e3bbe2f37..87aff09039 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -775,6 +775,26 @@ private:
* nTimeFirstKey more intelligently for more efficient rescans.
*/
bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ /** Add a KeyOriginInfo to the wallet */
+ bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
+
+ //! Adds a key to the store, and saves it to disk.
+ bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ //! 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_wallet);
+
+ void AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch);
+
+ bool SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose);
+
+ //! Adds a script to the store and saves it to disk
+ bool AddCScriptWithDB(WalletBatch& batch, const CScript& script);
+
+ //! Unsets a wallet flag and saves it to disk
+ void UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag);
/** Interface for accessing chain state. */
interfaces::Chain* m_chain;
@@ -833,8 +853,6 @@ public:
// Map from Script ID to key metadata (for watch-only keys).
std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_wallet);
- bool WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, bool overwrite);
-
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID = 0;
@@ -930,7 +948,6 @@ public:
CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
//! Load metadata (used by LoadWallet)
@@ -1049,6 +1066,11 @@ public:
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, bool use_max_sig = false) const;
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig = false) const;
+ bool ImportScripts(const std::set<CScript> scripts) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE};
@@ -1070,8 +1092,6 @@ public:
bool NewKeyPool();
size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool TopUpKeyPool(unsigned int kpSize = 0);
- void AddKeypoolPubkey(const CPubKey& pubkey, const bool internal);
- void AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch);
/**
* Reserves a key from the keypool and sets nIndex to its index
@@ -1288,9 +1308,6 @@ public:
/** Implement lookup of key origin information through wallet key metadata. */
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
-
- /** Add a KeyOriginInfo to the wallet */
- bool AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info);
};
/**
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 0532a55ff5..d4a3bba97a 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -143,9 +143,11 @@ public:
};
/** Access to the wallet database.
- * This represents a single transaction at the
- * database. It will be committed when the object goes out of scope.
- * Optionally (on by default) it will flush to disk as well.
+ * Opens the database and provides read and write access to it. Each read and write is its own transaction.
+ * Multiple operation transactions can be started using TxnBegin() and committed using TxnCommit()
+ * Otherwise the transaction will be committed when the object goes out of scope.
+ * Optionally (on by default) it will flush to disk on close.
+ * Every 1000 writes will automatically trigger a flush to disk.
*/
class WalletBatch
{
@@ -157,6 +159,9 @@ private:
return false;
}
m_database.IncrementUpdateCounter();
+ if (m_database.nUpdateCounter % 1000 == 0) {
+ m_batch.Flush();
+ }
return true;
}
@@ -167,6 +172,9 @@ private:
return false;
}
m_database.IncrementUpdateCounter();
+ if (m_database.nUpdateCounter % 1000 == 0) {
+ m_batch.Flush();
+ }
return true;
}
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 5c1b086703..c6132a2686 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -59,7 +59,7 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
try {
bool first_run;
load_wallet_ret = wallet_instance->LoadWallet(first_run);
- } catch (const std::runtime_error) {
+ } catch (const std::runtime_error&) {
fprintf(stderr, "Error loading %s. Is wallet being used by another process?\n", name.c_str());
return nullptr;
}
diff --git a/src/warnings.cpp b/src/warnings.cpp
index 1c6ba13f60..8ac9ee09db 100644
--- a/src/warnings.cpp
+++ b/src/warnings.cpp
@@ -8,10 +8,10 @@
#include <util/system.h>
#include <warnings.h>
-CCriticalSection cs_warnings;
-std::string strMiscWarning GUARDED_BY(cs_warnings);
-bool fLargeWorkForkFound GUARDED_BY(cs_warnings) = false;
-bool fLargeWorkInvalidChainFound GUARDED_BY(cs_warnings) = false;
+static RecursiveMutex cs_warnings;
+static std::string strMiscWarning GUARDED_BY(cs_warnings);
+static bool fLargeWorkForkFound GUARDED_BY(cs_warnings) = false;
+static bool fLargeWorkInvalidChainFound GUARDED_BY(cs_warnings) = false;
void SetMiscWarning(const std::string& strWarning)
{