aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/bdb.cpp152
-rw-r--r--src/wallet/bdb.h136
-rw-r--r--src/wallet/context.h2
-rw-r--r--src/wallet/db.h140
-rw-r--r--src/wallet/init.cpp12
-rw-r--r--src/wallet/load.cpp11
-rw-r--r--src/wallet/load.h3
-rw-r--r--src/wallet/rpcdump.cpp2
-rw-r--r--src/wallet/rpcwallet.cpp136
-rw-r--r--src/wallet/salvage.cpp6
-rw-r--r--src/wallet/scriptpubkeyman.cpp28
-rw-r--r--src/wallet/scriptpubkeyman.h6
-rw-r--r--src/wallet/test/init_test_fixture.cpp5
-rw-r--r--src/wallet/test/wallet_test_fixture.h5
-rw-r--r--src/wallet/test/wallet_tests.cpp38
-rw-r--r--src/wallet/wallet.cpp53
-rw-r--r--src/wallet/wallet.h20
-rw-r--r--src/wallet/walletdb.cpp58
-rw-r--r--src/wallet/walletdb.h21
-rw-r--r--src/wallet/wallettool.cpp6
20 files changed, 497 insertions, 343 deletions
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index fa4a505982..1953be2d54 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -139,7 +139,7 @@ BerkeleyEnvironment::~BerkeleyEnvironment()
Close();
}
-bool BerkeleyEnvironment::Open(bool retry)
+bool BerkeleyEnvironment::Open(bilingual_str& err)
{
if (fDbEnvInit) {
return true;
@@ -149,6 +149,7 @@ bool BerkeleyEnvironment::Open(bool retry)
TryCreateDirectories(pathIn);
if (!LockDirectory(pathIn, ".walletlock")) {
LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
+ err = strprintf(_("Error initializing wallet database environment %s!"), Directory());
return false;
}
@@ -188,23 +189,11 @@ bool BerkeleyEnvironment::Open(bool retry)
LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
}
Reset();
- if (retry) {
- // try moving the database env out of the way
- fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime());
- try {
- fs::rename(pathLogDir, pathDatabaseBak);
- LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string());
- } catch (const fs::filesystem_error&) {
- // failure is ok (well, not really, but it's not worse than what we started with)
- }
- // try opening it again one more time
- if (!Open(false /* retry */)) {
- // if it still fails, it probably means we can't even create the database env
- return false;
- }
- } else {
- return false;
+ err = strprintf(_("Error initializing wallet database environment %s!"), Directory());
+ if (ret == DB_RUNRECOVERY) {
+ err += Untranslated(" ") + _("This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet");
}
+ return false;
}
fDbEnvInit = true;
@@ -300,8 +289,7 @@ bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
LogPrintf("Using wallet %s\n", file_path.string());
- if (!env->Open(true /* retry */)) {
- errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
+ if (!env->Open(errorStr)) {
return false;
}
@@ -324,8 +312,17 @@ void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
dbenv->lsn_reset(strFile.c_str(), 0);
}
+BerkeleyDatabase::~BerkeleyDatabase()
+{
+ if (env) {
+ LOCK(cs_db);
+ size_t erased = env->m_databases.erase(strFile);
+ assert(erased == 1);
+ env->m_fileids.erase(strFile);
+ }
+}
-BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr)
+BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr), m_database(database)
{
fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
fFlushOnClose = fFlushOnCloseIn;
@@ -342,7 +339,8 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
{
LOCK(cs_db);
- if (!env->Open(false /* retry */))
+ bilingual_str open_err;
+ if (!env->Open(open_err))
throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
pdb = database.m_db.get();
@@ -399,11 +397,16 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
fReadOnly = fTmp;
}
}
- ++env->mapFileUseCount[strFilename];
+ database.AddRef();
strFile = strFilename;
}
}
+void BerkeleyDatabase::Open(const char* mode)
+{
+ throw std::logic_error("BerkeleyDatabase does not implement Open. This function should not be called.");
+}
+
void BerkeleyBatch::Flush()
{
if (activeTxn)
@@ -437,11 +440,7 @@ void BerkeleyBatch::Close()
if (fFlushOnClose)
Flush();
- {
- LOCK(cs_db);
- --env->mapFileUseCount[strFile];
- }
- env->m_db_in_use.notify_all();
+ m_database.RemoveRef();
}
void BerkeleyEnvironment::CloseDb(const std::string& strFile)
@@ -482,7 +481,8 @@ void BerkeleyEnvironment::ReloadDbEnv()
// Reset the environment
Flush(true); // This will flush and close the environment
Reset();
- Open(true);
+ bilingual_str open_err;
+ Open(open_err);
}
bool BerkeleyDatabase::Rewrite(const char* pszSkip)
@@ -615,42 +615,33 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
bool BerkeleyDatabase::PeriodicFlush()
{
- if (IsDummy()) {
- return true;
- }
- bool ret = false;
+ // There's nothing to do for dummy databases. Return true.
+ if (IsDummy()) return true;
+
+ // Don't flush if we can't acquire the lock.
TRY_LOCK(cs_db, lockDb);
- if (lockDb)
- {
- // Don't do this if any databases are in use
- int nRefCount = 0;
- std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin();
- while (mit != env->mapFileUseCount.end())
- {
- nRefCount += (*mit).second;
- mit++;
- }
+ if (!lockDb) return false;
- if (nRefCount == 0)
- {
- std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
- if (mi != env->mapFileUseCount.end())
- {
- LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
- int64_t nStart = GetTimeMillis();
+ // Don't flush if any databases are in use
+ for (const auto& use_count : env->mapFileUseCount) {
+ if (use_count.second > 0) return false;
+ }
- // Flush wallet file so it's self contained
- env->CloseDb(strFile);
- env->CheckpointLSN(strFile);
+ // Don't flush if there haven't been any batch writes for this database.
+ auto it = env->mapFileUseCount.find(strFile);
+ if (it == env->mapFileUseCount.end()) return false;
- env->mapFileUseCount.erase(mi++);
- LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
- ret = true;
- }
- }
- }
+ LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
+ int64_t nStart = GetTimeMillis();
+
+ // Flush wallet file so it's self contained
+ env->CloseDb(strFile);
+ env->CheckpointLSN(strFile);
+ env->mapFileUseCount.erase(it);
- return ret;
+ LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
+
+ return true;
}
bool BerkeleyDatabase::Backup(const std::string& strDest) const
@@ -694,22 +685,17 @@ bool BerkeleyDatabase::Backup(const std::string& strDest) const
}
}
-void BerkeleyDatabase::Flush(bool shutdown)
+void BerkeleyDatabase::Flush()
{
if (!IsDummy()) {
- env->Flush(shutdown);
- if (shutdown) {
- LOCK(cs_db);
- g_dbenvs.erase(env->Directory().string());
- env = nullptr;
- } else {
- // TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
- // first database shutdown when multiple databases are open in the same
- // environment, should replace raw database `env` pointers with shared or weak
- // pointers, or else separate the database and environment shutdowns so
- // environments can be shut down after databases.
- env->m_fileids.erase(strFile);
- }
+ env->Flush(false);
+ }
+}
+
+void BerkeleyDatabase::Close()
+{
+ if (!IsDummy()) {
+ env->Flush(true);
}
}
@@ -850,3 +836,23 @@ bool BerkeleyBatch::HasKey(CDataStream&& key)
int ret = pdb->exists(activeTxn, datKey, 0);
return ret == 0;
}
+
+void BerkeleyDatabase::AddRef()
+{
+ LOCK(cs_db);
+ ++env->mapFileUseCount[strFile];
+}
+
+void BerkeleyDatabase::RemoveRef()
+{
+ {
+ LOCK(cs_db);
+ --env->mapFileUseCount[strFile];
+ }
+ env->m_db_in_use.notify_all();
+}
+
+std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(const char* mode, bool flush_on_close)
+{
+ return MakeUnique<BerkeleyBatch>(*this, mode, flush_on_close);
+}
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index 599319482b..ef3b81d4d6 100644
--- a/src/wallet/bdb.h
+++ b/src/wallet/bdb.h
@@ -69,7 +69,7 @@ public:
bool Verify(const std::string& strFile);
- bool Open(bool retry);
+ bool Open(bilingual_str& error);
void Close();
void Flush(bool fShutdown);
void CheckpointLSN(const std::string& strFile);
@@ -90,62 +90,67 @@ public:
/** Get BerkeleyEnvironment and database filename given a wallet path. */
std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
-/** Return wheter a BDB wallet database is currently loaded. */
+/** Return whether a BDB wallet database is currently loaded. */
bool IsBDBWalletLoaded(const fs::path& wallet_path);
+class BerkeleyBatch;
+
/** An instance of this class represents one database.
* For BerkeleyDB this is just a (env, strFile) tuple.
**/
-class BerkeleyDatabase
+class BerkeleyDatabase : public WalletDatabase
{
friend class BerkeleyBatch;
public:
/** Create dummy DB handle */
- BerkeleyDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
+ BerkeleyDatabase() : WalletDatabase(), env(nullptr)
{
}
/** Create DB handle to real database */
BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) :
- nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(std::move(env)), strFile(std::move(filename))
+ WalletDatabase(), env(std::move(env)), strFile(std::move(filename))
{
auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
assert(inserted.second);
}
- ~BerkeleyDatabase() {
- if (env) {
- size_t erased = env->m_databases.erase(strFile);
- assert(erased == 1);
- }
- }
+ ~BerkeleyDatabase() override;
+
+ /** Open the database if it is not already opened.
+ * Dummy function, doesn't do anything right now, but is needed for class abstraction */
+ void Open(const char* mode) override;
/** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
*/
- bool Rewrite(const char* pszSkip=nullptr);
+ bool Rewrite(const char* pszSkip=nullptr) override;
+
+ /** Indicate the a new database user has began using the database. */
+ void AddRef() override;
+ /** Indicate that database user has stopped using the database and that it could be flushed or closed. */
+ void RemoveRef() override;
/** Back up the entire database to a file.
*/
- bool Backup(const std::string& strDest) const;
+ bool Backup(const std::string& strDest) const override;
- /** Make sure all changes are flushed to disk.
+ /** Make sure all changes are flushed to database file.
*/
- void Flush(bool shutdown);
+ void Flush() override;
+ /** Flush to the database file and close the database.
+ * Also close the environment if no other databases are open in it.
+ */
+ void Close() override;
/* flush the wallet passively (TRY_LOCK)
ideal to be called periodically */
- bool PeriodicFlush();
-
- void IncrementUpdateCounter();
+ bool PeriodicFlush() override;
- void ReloadDbEnv();
+ void IncrementUpdateCounter() override;
- std::atomic<unsigned int> nUpdateCounter;
- unsigned int nLastSeen;
- unsigned int nLastFlushed;
- int64_t nLastWalletUpdate;
+ void ReloadDbEnv() override;
/** Verifies the environment and database file */
- bool Verify(bilingual_str& error);
+ bool Verify(bilingual_str& error) override;
/**
* Pointer to shared database environment.
@@ -161,6 +166,9 @@ public:
/** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
std::unique_ptr<Db> m_db;
+ /** Make a BerkeleyBatch connected to this database */
+ std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) override;
+
private:
std::string strFile;
@@ -172,7 +180,7 @@ private:
};
/** RAII class that provides access to a Berkeley database */
-class BerkeleyBatch
+class BerkeleyBatch : public DatabaseBatch
{
/** RAII class that automatically cleanses its data on destruction */
class SafeDbt final
@@ -195,10 +203,10 @@ class BerkeleyBatch
};
private:
- bool ReadKey(CDataStream&& key, CDataStream& value);
- bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true);
- bool EraseKey(CDataStream&& key);
- bool HasKey(CDataStream&& key);
+ bool ReadKey(CDataStream&& key, CDataStream& value) override;
+ bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true) override;
+ bool EraseKey(CDataStream&& key) override;
+ bool HasKey(CDataStream&& key) override;
protected:
Db* pdb;
@@ -208,74 +216,24 @@ protected:
bool fReadOnly;
bool fFlushOnClose;
BerkeleyEnvironment *env;
+ BerkeleyDatabase& m_database;
public:
explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
- ~BerkeleyBatch() { Close(); }
+ ~BerkeleyBatch() override { Close(); }
BerkeleyBatch(const BerkeleyBatch&) = delete;
BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
- void Flush();
- void Close();
-
- template <typename K, typename T>
- bool Read(const K& key, T& value)
- {
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
-
- CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- if (!ReadKey(std::move(ssKey), ssValue)) return false;
- try {
- ssValue >> value;
- return true;
- } catch (const std::exception&) {
- return false;
- }
- }
-
- template <typename K, typename T>
- bool Write(const K& key, const T& value, bool fOverwrite = true)
- {
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
-
- CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- ssValue.reserve(10000);
- ssValue << value;
-
- return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite);
- }
-
- template <typename K>
- bool Erase(const K& key)
- {
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
-
- return EraseKey(std::move(ssKey));
- }
-
- template <typename K>
- bool Exists(const K& key)
- {
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- ssKey.reserve(1000);
- ssKey << key;
-
- return HasKey(std::move(ssKey));
- }
+ void Flush() override;
+ void Close() override;
- bool StartCursor();
- bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete);
- void CloseCursor();
- bool TxnBegin();
- bool TxnCommit();
- bool TxnAbort();
+ bool StartCursor() override;
+ bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override;
+ void CloseCursor() override;
+ bool TxnBegin() override;
+ bool TxnCommit() override;
+ bool TxnAbort() override;
};
std::string BerkeleyDatabaseVersion();
diff --git a/src/wallet/context.h b/src/wallet/context.h
index 3c8fdd1c59..a83591154f 100644
--- a/src/wallet/context.h
+++ b/src/wallet/context.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_WALLET_CONTEXT_H
#define BITCOIN_WALLET_CONTEXT_H
+class ArgsManager;
namespace interfaces {
class Chain;
} // namespace interfaces
@@ -21,6 +22,7 @@ class Chain;
//! behavior.
struct WalletContext {
interfaces::Chain* chain{nullptr};
+ ArgsManager* args{nullptr};
//! Declare default constructor and destructor that are not inline, so code
//! instantiating the WalletContext struct doesn't need to #include class
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 1322bf54fa..12dc1cc96b 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -6,12 +6,152 @@
#ifndef BITCOIN_WALLET_DB_H
#define BITCOIN_WALLET_DB_H
+#include <clientversion.h>
#include <fs.h>
+#include <streams.h>
+#include <atomic>
+#include <memory>
#include <string>
+struct bilingual_str;
+
/** Given a wallet directory path or legacy file path, return path to main data file in the wallet database. */
fs::path WalletDataFilePath(const fs::path& wallet_path);
void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);
+/** RAII class that provides access to a WalletDatabase */
+class DatabaseBatch
+{
+private:
+ virtual bool ReadKey(CDataStream&& key, CDataStream& value) = 0;
+ virtual bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite=true) = 0;
+ virtual bool EraseKey(CDataStream&& key) = 0;
+ virtual bool HasKey(CDataStream&& key) = 0;
+
+public:
+ explicit DatabaseBatch() {}
+ virtual ~DatabaseBatch() {}
+
+ DatabaseBatch(const DatabaseBatch&) = delete;
+ DatabaseBatch& operator=(const DatabaseBatch&) = delete;
+
+ virtual void Flush() = 0;
+ virtual void Close() = 0;
+
+ template <typename K, typename T>
+ bool Read(const K& key, T& value)
+ {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+
+ CDataStream ssValue(SER_DISK, CLIENT_VERSION);
+ if (!ReadKey(std::move(ssKey), ssValue)) return false;
+ try {
+ ssValue >> value;
+ return true;
+ } catch (const std::exception&) {
+ return false;
+ }
+ }
+
+ template <typename K, typename T>
+ bool Write(const K& key, const T& value, bool fOverwrite = true)
+ {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+
+ CDataStream ssValue(SER_DISK, CLIENT_VERSION);
+ ssValue.reserve(10000);
+ ssValue << value;
+
+ return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite);
+ }
+
+ template <typename K>
+ bool Erase(const K& key)
+ {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+
+ return EraseKey(std::move(ssKey));
+ }
+
+ template <typename K>
+ bool Exists(const K& key)
+ {
+ CDataStream ssKey(SER_DISK, CLIENT_VERSION);
+ ssKey.reserve(1000);
+ ssKey << key;
+
+ return HasKey(std::move(ssKey));
+ }
+
+ virtual bool StartCursor() = 0;
+ virtual bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) = 0;
+ virtual void CloseCursor() = 0;
+ virtual bool TxnBegin() = 0;
+ virtual bool TxnCommit() = 0;
+ virtual bool TxnAbort() = 0;
+};
+
+/** An instance of this class represents one database.
+ **/
+class WalletDatabase
+{
+public:
+ /** Create dummy DB handle */
+ WalletDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0) {}
+ virtual ~WalletDatabase() {};
+
+ /** Open the database if it is not already opened. */
+ virtual void Open(const char* mode) = 0;
+
+ //! Counts the number of active database users to be sure that the database is not closed while someone is using it
+ std::atomic<int> m_refcount{0};
+ /** Indicate the a new database user has began using the database. Increments m_refcount */
+ virtual void AddRef() = 0;
+ /** Indicate that database user has stopped using the database and that it could be flushed or closed. Decrement m_refcount */
+ virtual void RemoveRef() = 0;
+
+ /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
+ */
+ virtual bool Rewrite(const char* pszSkip=nullptr) = 0;
+
+ /** Back up the entire database to a file.
+ */
+ virtual bool Backup(const std::string& strDest) const = 0;
+
+ /** Make sure all changes are flushed to database file.
+ */
+ virtual void Flush() = 0;
+ /** Flush to the database file and close the database.
+ * Also close the environment if no other databases are open in it.
+ */
+ virtual void Close() = 0;
+ /* flush the wallet passively (TRY_LOCK)
+ ideal to be called periodically */
+ virtual bool PeriodicFlush() = 0;
+
+ virtual void IncrementUpdateCounter() = 0;
+
+ virtual void ReloadDbEnv() = 0;
+
+ std::atomic<unsigned int> nUpdateCounter;
+ unsigned int nLastSeen;
+ unsigned int nLastFlushed;
+ int64_t nLastWalletUpdate;
+
+ /** Verifies the environment and database file */
+ virtual bool Verify(bilingual_str& error) = 0;
+
+ std::string m_file_path;
+
+ /** Make a DatabaseBatch connected to this database */
+ virtual std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) = 0;
+};
+
#endif // BITCOIN_WALLET_DB_H
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index f173b5e62b..781920755c 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -9,6 +9,7 @@
#include <node/context.h>
#include <node/ui_interface.h>
#include <outputtype.h>
+#include <util/check.h>
#include <util/moneystr.h>
#include <util/system.h>
#include <util/translation.h>
@@ -16,9 +17,9 @@
#include <wallet/wallet.h>
#include <walletinitinterface.h>
-class WalletInit : public WalletInitInterface {
+class WalletInit : public WalletInitInterface
+{
public:
-
//! Was the wallet component compiled in.
bool HasWalletSupport() const override {return true;}
@@ -112,10 +113,11 @@ bool WalletInit::ParameterInteraction() const
void WalletInit::Construct(NodeContext& node) const
{
- if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
+ ArgsManager& args = *Assert(node.args);
+ if (args.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
LogPrintf("Wallet disabled!\n");
return;
}
- gArgs.SoftSetArg("-wallet", "");
- node.chain_clients.emplace_back(interfaces::MakeWalletClient(*node.chain, gArgs.GetArgs("-wallet")));
+ args.SoftSetArg("-wallet", "");
+ node.chain_clients.emplace_back(interfaces::MakeWalletClient(*node.chain, args, args.GetArgs("-wallet")));
}
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 8df3e78215..2a81d30133 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -11,6 +11,7 @@
#include <util/system.h>
#include <util/translation.h>
#include <wallet/wallet.h>
+#include <wallet/walletdb.h>
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
{
@@ -82,28 +83,30 @@ bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& walle
}
}
-void StartWallets(CScheduler& scheduler)
+void StartWallets(CScheduler& scheduler, const ArgsManager& args)
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
pwallet->postInitProcess();
}
// Schedule periodic wallet flushes and tx rebroadcasts
- scheduler.scheduleEvery(MaybeCompactWalletDB, std::chrono::milliseconds{500});
+ if (args.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
+ scheduler.scheduleEvery(MaybeCompactWalletDB, std::chrono::milliseconds{500});
+ }
scheduler.scheduleEvery(MaybeResendWalletTxs, std::chrono::milliseconds{1000});
}
void FlushWallets()
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
- pwallet->Flush(false);
+ pwallet->Flush();
}
}
void StopWallets()
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
- pwallet->Flush(true);
+ pwallet->Close();
}
}
diff --git a/src/wallet/load.h b/src/wallet/load.h
index e24b1f2e69..ff4f5b4b23 100644
--- a/src/wallet/load.h
+++ b/src/wallet/load.h
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
+class ArgsManager;
class CScheduler;
namespace interfaces {
@@ -22,7 +23,7 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
//! Complete startup of wallets.
-void StartWallets(CScheduler& scheduler);
+void StartWallets(CScheduler& scheduler, const ArgsManager& args);
//! Flush all wallets in preparation for shutdown.
void FlushWallets();
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index c9ea6c2ad9..3b752ca936 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1547,7 +1547,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
if (!w_desc.descriptor->GetOutputType()) {
warnings.push_back("Unknown output type, cannot set descriptor to active.");
} else {
- pwallet->SetActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
+ pwallet->AddActiveScriptPubKeyMan(spk_manager->GetID(), *w_desc.descriptor->GetOutputType(), internal);
}
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 55114a17d7..9d334063c4 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -359,36 +359,54 @@ static UniValue setlabel(const JSONRPCRequest& request)
return NullUniValue;
}
+void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_fee_outputs, std::vector<CRecipient> &recipients) {
+ std::set<CTxDestination> destinations;
+ int i = 0;
+ for (const std::string& address: address_amounts.getKeys()) {
+ CTxDestination dest = DecodeDestination(address);
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + address);
+ }
-static CTransactionRef SendMoney(CWallet* const pwallet, const CTxDestination& address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
-{
- CAmount curBalance = pwallet->GetBalance(0, coin_control.m_avoid_address_reuse).m_mine_trusted;
+ if (destinations.count(dest)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + address);
+ }
+ destinations.insert(dest);
- // Check amount
- if (nValue <= 0)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
+ CScript script_pub_key = GetScriptForDestination(dest);
+ CAmount amount = AmountFromValue(address_amounts[i++]);
- if (nValue > curBalance)
- throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
+ bool subtract_fee = false;
+ for (unsigned int idx = 0; idx < subtract_fee_outputs.size(); idx++) {
+ const UniValue& addr = subtract_fee_outputs[idx];
+ if (addr.get_str() == address) {
+ subtract_fee = true;
+ }
+ }
- // Parse Bitcoin address
- CScript scriptPubKey = GetScriptForDestination(address);
+ CRecipient recipient = {script_pub_key, amount, subtract_fee};
+ recipients.push_back(recipient);
+ }
+}
+
+UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value)
+{
+ EnsureWalletIsUnlocked(pwallet);
- // Create and send the transaction
+ // Shuffle recipient list
+ std::shuffle(recipients.begin(), recipients.end(), FastRandomContext());
+
+ // Send
CAmount nFeeRequired = 0;
- bilingual_str error;
- std::vector<CRecipient> vecSend;
int nChangePosRet = -1;
- CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
- vecSend.push_back(recipient);
+ bilingual_str error;
CTransactionRef tx;
- if (!pwallet->CreateTransaction(vecSend, tx, nFeeRequired, nChangePosRet, error, coin_control)) {
- if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
- error = strprintf(Untranslated("Error: This transaction requires a transaction fee of at least %s"), FormatMoney(nFeeRequired));
- throw JSONRPCError(RPC_WALLET_ERROR, error.original);
+ bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ if (!fCreated) {
+ throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
}
- pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */);
- return tx;
+ pwallet->CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
+ return tx->GetHash().GetHex();
}
static UniValue sendtoaddress(const JSONRPCRequest& request)
@@ -436,16 +454,6 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
- CTxDestination dest = DecodeDestination(request.params[0].get_str());
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
- }
-
- // Amount
- CAmount nAmount = AmountFromValue(request.params[1]);
- if (nAmount <= 0)
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
-
// Wallet comments
mapValue_t mapValue;
if (!request.params[2].isNull() && !request.params[2].get_str().empty())
@@ -471,8 +479,18 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
- return tx->GetHash().GetHex();
+ UniValue address_amounts(UniValue::VOBJ);
+ const std::string address = request.params[0].get_str();
+ address_amounts.pushKV(address, request.params[1]);
+ UniValue subtractFeeFromAmount(UniValue::VARR);
+ if (fSubtractFeeFromAmount) {
+ subtractFeeFromAmount.push_back(address);
+ }
+
+ std::vector<CRecipient> recipients;
+ ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
+
+ return SendMoney(pwallet, coin_control, recipients, mapValue);
}
static UniValue listaddressgroupings(const JSONRPCRequest& request)
@@ -860,52 +878,10 @@ static UniValue sendmany(const JSONRPCRequest& request)
SetFeeEstimateMode(pwallet, coin_control, request.params[7], request.params[6]);
- std::set<CTxDestination> destinations;
- std::vector<CRecipient> vecSend;
+ std::vector<CRecipient> recipients;
+ ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
- std::vector<std::string> keys = sendTo.getKeys();
- for (const std::string& name_ : keys) {
- CTxDestination dest = DecodeDestination(name_);
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
- }
-
- if (destinations.count(dest)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
- }
- destinations.insert(dest);
-
- CScript scriptPubKey = GetScriptForDestination(dest);
- CAmount nAmount = AmountFromValue(sendTo[name_]);
- if (nAmount <= 0)
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
-
- bool fSubtractFeeFromAmount = false;
- for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
- const UniValue& addr = subtractFeeFromAmount[idx];
- if (addr.get_str() == name_)
- fSubtractFeeFromAmount = true;
- }
-
- CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
- vecSend.push_back(recipient);
- }
-
- EnsureWalletIsUnlocked(pwallet);
-
- // Shuffle recipient list
- std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
-
- // Send
- CAmount nFeeRequired = 0;
- int nChangePosRet = -1;
- bilingual_str error;
- CTransactionRef tx;
- bool fCreated = pwallet->CreateTransaction(vecSend, tx, nFeeRequired, nChangePosRet, error, coin_control);
- if (!fCreated)
- throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
- pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */);
- return tx->GetHash().GetHex();
+ return SendMoney(pwallet, coin_control, recipients, std::move(mapValue));
}
static UniValue addmultisigaddress(const JSONRPCRequest& request)
@@ -3141,7 +3117,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
CAmount fee;
int change_position;
CCoinControl coin_control;
- // Automatically select (additional) coins. Can be overriden by options.add_inputs.
+ // Automatically select (additional) coins. Can be overridden by options.add_inputs.
coin_control.m_add_inputs = true;
FundTransaction(pwallet, tx, fee, change_position, request.params[1], coin_control);
@@ -4075,7 +4051,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
CCoinControl coin_control;
// Automatically select coins, unless at least one is manually selected. Can
- // be overriden by options.add_inputs.
+ // be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3], coin_control);
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
index e6e62332c0..af57210f01 100644
--- a/src/wallet/salvage.cpp
+++ b/src/wallet/salvage.cpp
@@ -5,6 +5,7 @@
#include <fs.h>
#include <streams.h>
+#include <util/translation.h>
#include <wallet/salvage.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
@@ -20,8 +21,9 @@ bool RecoverDatabaseFile(const fs::path& file_path)
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
- if (!env->Open(true /* retry */)) {
- tfm::format(std::cerr, "Error initializing wallet database environment %s!", env->Directory());
+ bilingual_str open_err;
+ if (!env->Open(open_err)) {
+ tfm::format(std::cerr, "%s\n", open_err.original);
return false;
}
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 38d94335a3..51715462c5 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -905,20 +905,22 @@ bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest, int64_t nCreateTim
return AddWatchOnly(dest);
}
-void LegacyScriptPubKeyMan::SetHDChain(const CHDChain& chain, bool memonly)
+void LegacyScriptPubKeyMan::LoadHDChain(const CHDChain& chain)
{
LOCK(cs_KeyStore);
- // memonly == true means we are loading the wallet file
- // memonly == false means that the chain is actually being changed
- if (!memonly) {
- // Store the new chain
- if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(chain)) {
- throw std::runtime_error(std::string(__func__) + ": writing chain failed");
- }
- // When there's an old chain, add it as an inactive chain as we are now rotating hd chains
- if (!m_hd_chain.seed_id.IsNull()) {
- AddInactiveHDChain(m_hd_chain);
- }
+ m_hd_chain = chain;
+}
+
+void LegacyScriptPubKeyMan::AddHDChain(const CHDChain& chain)
+{
+ LOCK(cs_KeyStore);
+ // Store the new chain
+ if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(chain)) {
+ throw std::runtime_error(std::string(__func__) + ": writing chain failed");
+ }
+ // When there's an old chain, add it as an inactive chain as we are now rotating hd chains
+ if (!m_hd_chain.seed_id.IsNull()) {
+ AddInactiveHDChain(m_hd_chain);
}
m_hd_chain = chain;
@@ -1172,7 +1174,7 @@ void LegacyScriptPubKeyMan::SetHDSeed(const CPubKey& seed)
CHDChain newHdChain;
newHdChain.nVersion = m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE;
newHdChain.seed_id = seed.GetID();
- SetHDChain(newHdChain, false);
+ AddHDChain(newHdChain);
NotifyCanGetAddressesChanged();
WalletBatch batch(m_storage.GetDatabase());
m_storage.UnsetBlankWalletFlag(batch);
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 9fa2a68284..a96d971734 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -422,8 +422,10 @@ public:
//! 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) */
- void SetHDChain(const CHDChain& chain, bool memonly);
+ /* 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);
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 797a0d634f..35bd965673 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -3,13 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
+#include <util/check.h>
#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
-InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName): BasicTestingSetup(chainName)
+InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
- m_chain_client = MakeWalletClient(*m_chain, {});
+ m_chain_client = MakeWalletClient(*m_chain, *Assert(m_node.args), {});
std::string sep;
sep += fs::path::preferred_separator;
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index 6c32868b1e..99d7cfe921 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -10,17 +10,18 @@
#include <interfaces/chain.h>
#include <interfaces/wallet.h>
#include <node/context.h>
+#include <util/check.h>
#include <wallet/wallet.h>
#include <memory>
/** Testing setup and teardown for wallet.
*/
-struct WalletTestingSetup: public TestingSetup {
+struct WalletTestingSetup : public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
- std::unique_ptr<interfaces::ChainClient> m_chain_client = interfaces::MakeWalletClient(*m_chain, {});
+ std::unique_ptr<interfaces::ChainClient> m_chain_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args), {});
CWallet m_wallet;
std::unique_ptr<interfaces::Handler> m_chain_notifications_handler;
};
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 5c565a3d38..d2770a46f7 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -28,6 +28,11 @@ extern UniValue importmulti(const JSONRPCRequest& request);
extern UniValue dumpwallet(const JSONRPCRequest& request);
extern UniValue importwallet(const JSONRPCRequest& request);
+// Ensure that fee levels defined in the wallet are at least as high
+// as the default levels for node policy.
+static_assert(DEFAULT_TRANSACTION_MINFEE >= DEFAULT_MIN_RELAY_TX_FEE, "wallet minimum fee is smaller than default relay fee");
+static_assert(WALLET_INCREMENTAL_RELAY_FEE >= DEFAULT_INCREMENTAL_RELAY_FEE, "wallet incremental fee is smaller than default incremental relay fee");
+
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
static std::shared_ptr<CWallet> TestLoadWallet(interfaces::Chain& chain)
@@ -791,4 +796,37 @@ BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup)
TestUnloadWallet(std::move(wallet));
}
+BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup)
+{
+ auto chain = interfaces::MakeChain(m_node);
+ auto wallet = TestLoadWallet(*chain);
+ CKey key;
+ key.MakeNewKey(true);
+ AddKey(*wallet, key);
+
+ std::string error;
+ m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+ auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
+ CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
+
+ SyncWithValidationInterfaceQueue();
+
+ {
+ auto block_hash = block_tx.GetHash();
+ auto prev_hash = m_coinbase_txns[0]->GetHash();
+
+ LOCK(wallet->cs_wallet);
+ BOOST_CHECK(wallet->HasWalletSpend(prev_hash));
+ BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 1u);
+
+ std::vector<uint256> vHashIn{ block_hash }, vHashOut;
+ BOOST_CHECK_EQUAL(wallet->ZapSelectTx(vHashIn, vHashOut), DBErrors::LOAD_OK);
+
+ BOOST_CHECK(!wallet->HasWalletSpend(prev_hash));
+ BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 0u);
+ }
+
+ TestUnloadWallet(std::move(wallet));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 29ff7bbef1..cee2f2214c 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -439,9 +439,14 @@ bool CWallet::HasWalletSpend(const uint256& txid) const
return (iter != mapTxSpends.end() && iter->first.hash == txid);
}
-void CWallet::Flush(bool shutdown)
+void CWallet::Flush()
{
- database->Flush(shutdown);
+ database->Flush();
+}
+
+void CWallet::Close()
+{
+ database->Close();
}
void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> range)
@@ -1422,19 +1427,28 @@ bool CWallet::IsWalletFlagSet(uint64_t flag) const
return (m_wallet_flags & flag);
}
-bool CWallet::SetWalletFlags(uint64_t overwriteFlags, bool memonly)
+bool CWallet::LoadWalletFlags(uint64_t flags)
{
LOCK(cs_wallet);
- m_wallet_flags = overwriteFlags;
- if (((overwriteFlags & KNOWN_WALLET_FLAGS) >> 32) ^ (overwriteFlags >> 32)) {
+ if (((flags & KNOWN_WALLET_FLAGS) >> 32) ^ (flags >> 32)) {
// contains unknown non-tolerable wallet flags
return false;
}
- if (!memonly && !WalletBatch(*database).WriteWalletFlags(m_wallet_flags)) {
+ m_wallet_flags = flags;
+
+ return true;
+}
+
+bool CWallet::AddWalletFlags(uint64_t flags)
+{
+ LOCK(cs_wallet);
+ // We should never be writing unknown non-tolerable wallet flags
+ assert(((flags & KNOWN_WALLET_FLAGS) >> 32) == (flags >> 32));
+ if (!WalletBatch(*database).WriteWalletFlags(flags)) {
throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
}
- return true;
+ return LoadWalletFlags(flags);
}
int64_t CWalletTx::GetTxTime() const
@@ -3120,9 +3134,11 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
{
AssertLockHeld(cs_wallet);
DBErrors nZapSelectTxRet = WalletBatch(*database, "cr+").ZapSelectTx(vHashIn, vHashOut);
- for (uint256 hash : vHashOut) {
+ for (const uint256& hash : vHashOut) {
const auto& it = mapWallet.find(hash);
wtxOrdered.erase(it->second.m_it_wtxOrdered);
+ for (const auto& txin : it->second.tx->vin)
+ mapTxSpends.erase(txin.prevout);
mapWallet.erase(it);
NotifyTransactionChanged(this, hash, CT_DELETED);
}
@@ -3796,7 +3812,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key
walletInstance->SetMinVersion(FEATURE_LATEST);
- walletInstance->SetWalletFlags(wallet_creation_flags, false);
+ walletInstance->AddWalletFlags(wallet_creation_flags);
// Only create LegacyScriptPubKeyMan when not descriptor wallet
if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
@@ -4417,12 +4433,21 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
spk_manager->SetupDescriptorGeneration(master_key, t);
uint256 id = spk_manager->GetID();
m_spk_managers[id] = std::move(spk_manager);
- SetActiveScriptPubKeyMan(id, t, internal);
+ AddActiveScriptPubKeyMan(id, t, internal);
}
}
}
-void CWallet::SetActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal, bool memonly)
+void CWallet::AddActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal)
+{
+ WalletBatch batch(*database);
+ if (!batch.WriteActiveScriptPubKeyMan(static_cast<uint8_t>(type), id, internal)) {
+ throw std::runtime_error(std::string(__func__) + ": writing active ScriptPubKeyMan id failed");
+ }
+ LoadActiveScriptPubKeyMan(id, type, internal);
+}
+
+void CWallet::LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal)
{
WalletLogPrintf("Setting spkMan to active: id = %s, type = %d, internal = %d\n", id.ToString(), static_cast<int>(type), static_cast<int>(internal));
auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;
@@ -4430,12 +4455,6 @@ void CWallet::SetActiveScriptPubKeyMan(uint256 id, OutputType type, bool interna
spk_man->SetInternal(internal);
spk_mans[type] = spk_man;
- if (!memonly) {
- WalletBatch batch(*database);
- if (!batch.WriteActiveScriptPubKeyMan(static_cast<uint8_t>(type), id, internal)) {
- throw std::runtime_error(std::string(__func__) + ": writing active ScriptPubKeyMan id failed");
- }
- }
NotifyCanGetAddressesChanged();
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 32d8481cd8..a761caf38c 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -1087,7 +1087,10 @@ public:
bool HasWalletSpend(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Flush wallet (bitdb flush)
- void Flush(bool shutdown=false);
+ void Flush();
+
+ //! Close wallet database
+ void Close();
/** Wallet is about to be unloaded */
boost::signals2::signal<void ()> NotifyUnload;
@@ -1176,7 +1179,9 @@ public:
/** overwrite all flags by the given uint64_t
returns false if unknown, non-tolerable flags are present */
- bool SetWalletFlags(uint64_t overwriteFlags, bool memOnly);
+ bool AddWalletFlags(uint64_t flags);
+ /** Loads the flags into the wallet. (used by LoadWallet) */
+ bool LoadWalletFlags(uint64_t flags);
/** Determine if we are a legacy wallet */
bool IsLegacy() const;
@@ -1254,12 +1259,17 @@ public:
//! Instantiate a descriptor ScriptPubKeyMan from the WalletDescriptor and load it
void LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc);
- //! Sets the active ScriptPubKeyMan for the specified type and internal
+ //! Adds the active ScriptPubKeyMan for the specified type and internal. Writes it to the wallet file
+ //! @param[in] id The unique id for the ScriptPubKeyMan
+ //! @param[in] type The OutputType this ScriptPubKeyMan provides addresses for
+ //! @param[in] internal Whether this ScriptPubKeyMan provides change addresses
+ void AddActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal);
+
+ //! Loads an active ScriptPubKeyMan for the specified type and internal. (used by LoadWallet)
//! @param[in] id The unique id for the ScriptPubKeyMan
//! @param[in] type The OutputType this ScriptPubKeyMan provides addresses for
//! @param[in] internal Whether this ScriptPubKeyMan provides change addresses
- //! @param[in] memonly Whether to record this update to the database. Set to true for wallet loading, normally false when actually updating the wallet.
- void SetActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal, bool memonly = false);
+ void LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal);
//! Create new DescriptorScriptPubKeyMans and add them to the wallet
void SetupDescriptorScriptPubKeyMans();
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 7da477d5b7..8c409b40cd 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -121,7 +121,7 @@ bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
if (!WriteIC(key, std::make_pair(vchCryptedSecret, checksum), false)) {
// It may already exist, so try writing just the checksum
std::vector<unsigned char> val;
- if (!m_batch.Read(key, val)) {
+ if (!m_batch->Read(key, val)) {
return false;
}
if (!WriteIC(key, std::make_pair(val, checksum), true)) {
@@ -166,8 +166,8 @@ bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
{
- if (m_batch.Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
- return m_batch.Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
+ if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
+ return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
}
bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
@@ -177,7 +177,7 @@ bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
{
- return m_batch.Read(std::make_pair(DBKeys::POOL, nPool), keypool);
+ return m_batch->Read(std::make_pair(DBKeys::POOL, nPool), keypool);
}
bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
@@ -539,11 +539,11 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
} else if (strType == DBKeys::HDCHAIN) {
CHDChain chain;
ssValue >> chain;
- pwallet->GetOrCreateLegacyScriptPubKeyMan()->SetHDChain(chain, true);
+ pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain);
} else if (strType == DBKeys::FLAGS) {
uint64_t flags;
ssValue >> flags;
- if (!pwallet->SetWalletFlags(flags, true)) {
+ if (!pwallet->LoadWalletFlags(flags)) {
strErr = "Error reading wallet database: Unknown non-tolerable wallet flags found";
return false;
}
@@ -592,9 +592,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssValue >> ser_xpub;
CExtPubKey xpub;
xpub.Decode(ser_xpub.data());
- if (wss.m_descriptor_caches.count(desc_id)) {
- wss.m_descriptor_caches[desc_id] = DescriptorCache();
- }
if (parent) {
wss.m_descriptor_caches[desc_id].CacheParentExtPubKey(key_exp_index, xpub);
} else {
@@ -693,14 +690,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
LOCK(pwallet->cs_wallet);
try {
int nMinVersion = 0;
- if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
+ if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
pwallet->LoadMinVersion(nMinVersion);
}
// Get cursor
- if (!m_batch.StartCursor())
+ if (!m_batch->StartCursor())
{
pwallet->WalletLogPrintf("Error getting wallet database cursor\n");
return DBErrors::CORRUPT;
@@ -712,13 +709,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
bool complete;
- bool ret = m_batch.ReadAtCursor(ssKey, ssValue, complete);
+ bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
if (complete) {
break;
}
else if (!ret)
{
- m_batch.CloseCursor();
+ m_batch->CloseCursor();
pwallet->WalletLogPrintf("Error reading next record from wallet database\n");
return DBErrors::CORRUPT;
}
@@ -748,14 +745,14 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
} catch (...) {
result = DBErrors::CORRUPT;
}
- m_batch.CloseCursor();
+ m_batch->CloseCursor();
// Set the active ScriptPubKeyMans
for (auto spk_man_pair : wss.m_active_external_spks) {
- pwallet->SetActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ false, /* memonly */ true);
+ pwallet->LoadActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ false);
}
for (auto spk_man_pair : wss.m_active_internal_spks) {
- pwallet->SetActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ true, /* memonly */ true);
+ pwallet->LoadActiveScriptPubKeyMan(spk_man_pair.second, spk_man_pair.first, /* internal */ true);
}
// Set the descriptor caches
@@ -785,7 +782,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
// Last client version to open this wallet, was previously the file version number
int last_client = CLIENT_VERSION;
- m_batch.Read(DBKeys::VERSION, last_client);
+ m_batch->Read(DBKeys::VERSION, last_client);
int wallet_version = pwallet->GetVersion();
pwallet->WalletLogPrintf("Wallet File Version = %d\n", wallet_version > 0 ? wallet_version : last_client);
@@ -810,7 +807,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
return DBErrors::NEED_REWRITE;
if (last_client < CLIENT_VERSION) // Update
- m_batch.Write(DBKeys::VERSION, CLIENT_VERSION);
+ m_batch->Write(DBKeys::VERSION, CLIENT_VERSION);
if (wss.fAnyUnordered)
result = pwallet->ReorderTransactions();
@@ -846,13 +843,13 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
try {
int nMinVersion = 0;
- if (m_batch.Read(DBKeys::MINVERSION, nMinVersion)) {
+ if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
if (nMinVersion > FEATURE_LATEST)
return DBErrors::TOO_NEW;
}
// Get cursor
- if (!m_batch.StartCursor())
+ if (!m_batch->StartCursor())
{
LogPrintf("Error getting wallet database cursor\n");
return DBErrors::CORRUPT;
@@ -864,11 +861,11 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
bool complete;
- bool ret = m_batch.ReadAtCursor(ssKey, ssValue, complete);
+ bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
if (complete) {
break;
} else if (!ret) {
- m_batch.CloseCursor();
+ m_batch->CloseCursor();
LogPrintf("Error reading next record from wallet database\n");
return DBErrors::CORRUPT;
}
@@ -886,7 +883,7 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
} catch (...) {
result = DBErrors::CORRUPT;
}
- m_batch.CloseCursor();
+ m_batch->CloseCursor();
return result;
}
@@ -952,9 +949,6 @@ void MaybeCompactWalletDB()
if (fOneThread.exchange(true)) {
return;
}
- if (!gArgs.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
- return;
- }
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
WalletDatabase& dbh = pwallet->GetDBHandle();
@@ -999,17 +993,17 @@ bool WalletBatch::WriteWalletFlags(const uint64_t flags)
bool WalletBatch::TxnBegin()
{
- return m_batch.TxnBegin();
+ return m_batch->TxnBegin();
}
bool WalletBatch::TxnCommit()
{
- return m_batch.TxnCommit();
+ return m_batch->TxnCommit();
}
bool WalletBatch::TxnAbort()
{
- return m_batch.TxnAbort();
+ return m_batch->TxnAbort();
}
bool IsWalletLoaded(const fs::path& wallet_path)
@@ -1018,20 +1012,20 @@ bool IsWalletLoaded(const fs::path& wallet_path)
}
/** Return object for accessing database at specified path. */
-std::unique_ptr<BerkeleyDatabase> CreateWalletDatabase(const fs::path& path)
+std::unique_ptr<WalletDatabase> CreateWalletDatabase(const fs::path& path)
{
std::string filename;
return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
}
/** Return object for accessing dummy database with no read/write capabilities. */
-std::unique_ptr<BerkeleyDatabase> CreateDummyWalletDatabase()
+std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase()
{
return MakeUnique<BerkeleyDatabase>();
}
/** Return object for accessing temporary in-memory database. */
-std::unique_ptr<BerkeleyDatabase> CreateMockWalletDatabase()
+std::unique_ptr<WalletDatabase> CreateMockWalletDatabase()
{
return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
}
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 61e0f19e56..7c5bf7652b 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -40,9 +40,6 @@ class CWalletTx;
class uint160;
class uint256;
-/** Backend-agnostic database type. */
-using WalletDatabase = BerkeleyDatabase;
-
/** Error statuses for the wallet database */
enum class DBErrors
{
@@ -183,12 +180,12 @@ private:
template <typename K, typename T>
bool WriteIC(const K& key, const T& value, bool fOverwrite = true)
{
- if (!m_batch.Write(key, value, fOverwrite)) {
+ if (!m_batch->Write(key, value, fOverwrite)) {
return false;
}
m_database.IncrementUpdateCounter();
if (m_database.nUpdateCounter % 1000 == 0) {
- m_batch.Flush();
+ m_batch->Flush();
}
return true;
}
@@ -196,19 +193,19 @@ private:
template <typename K>
bool EraseIC(const K& key)
{
- if (!m_batch.Erase(key)) {
+ if (!m_batch->Erase(key)) {
return false;
}
m_database.IncrementUpdateCounter();
if (m_database.nUpdateCounter % 1000 == 0) {
- m_batch.Flush();
+ m_batch->Flush();
}
return true;
}
public:
explicit WalletBatch(WalletDatabase& database, const char* pszMode = "r+", bool _fFlushOnClose = true) :
- m_batch(database, pszMode, _fFlushOnClose),
+ m_batch(database.MakeBatch(pszMode, _fFlushOnClose)),
m_database(database)
{
}
@@ -280,7 +277,7 @@ public:
//! Abort current transaction
bool TxnAbort();
private:
- BerkeleyBatch m_batch;
+ std::unique_ptr<DatabaseBatch> m_batch;
WalletDatabase& m_database;
};
@@ -294,12 +291,12 @@ bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, st
bool IsWalletLoaded(const fs::path& wallet_path);
/** Return object for accessing database at specified path. */
-std::unique_ptr<BerkeleyDatabase> CreateWalletDatabase(const fs::path& path);
+std::unique_ptr<WalletDatabase> CreateWalletDatabase(const fs::path& path);
/** Return object for accessing dummy database with no read/write capabilities. */
-std::unique_ptr<BerkeleyDatabase> CreateDummyWalletDatabase();
+std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase();
/** Return object for accessing temporary in-memory database. */
-std::unique_ptr<BerkeleyDatabase> CreateMockWalletDatabase();
+std::unique_ptr<WalletDatabase> CreateMockWalletDatabase();
#endif // BITCOIN_WALLET_WALLETDB_H
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 8a45d81456..9f25b1ae7d 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -17,7 +17,7 @@ namespace WalletTool {
static void WalletToolReleaseWallet(CWallet* wallet)
{
wallet->WalletLogPrintf("Releasing wallet\n");
- wallet->Flush(true);
+ wallet->Close();
delete wallet;
}
@@ -133,7 +133,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
std::shared_ptr<CWallet> wallet_instance = CreateWallet(name, path);
if (wallet_instance) {
WalletShowInfo(wallet_instance.get());
- wallet_instance->Flush(true);
+ wallet_instance->Close();
}
} else if (command == "info" || command == "salvage") {
if (!fs::exists(path)) {
@@ -145,7 +145,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
- wallet_instance->Flush(true);
+ wallet_instance->Close();
} else if (command == "salvage") {
return SalvageWallet(path);
}