Merge #19619: Remove wallet.dat path handling from wallet.cpp, rpcwallet.cpp

7bf6dfbb48 wallet: Remove path checking code from bitcoin-wallet tool (Russell Yanofsky)
77d5bb72b8 wallet: Remove path checking code from createwallet RPC (Russell Yanofsky)
a987438e9d wallet: Remove path checking code from loadwallet RPC (Russell Yanofsky)
8b5e7297c0 refactor: Pass wallet database into CWallet::Create (Russell Yanofsky)
3c815cfe54 wallet: Remove Verify and IsLoaded methods (Russell Yanofsky)
0d94e60625 refactor: Use DatabaseStatus and DatabaseOptions types (Russell Yanofsky)
b5b414151a wallet: Add MakeDatabase function (Russell Yanofsky)
288b4ffb6b Remove WalletLocation class (Russell Yanofsky)

Pull request description:

  Get rid of file path handling in wallet application code and move it down to database layer.

  There is no change in behavior except for some changed error messages.

  Motivation for this change is to make code more understandable, but also to prepare for adding SQLite support in #19077 so SQLite implementation can be contained at the database layer and wallet loading code does not need to become more complicated.

ACKs for top commit:
  achow101:
    ACK 7bf6dfbb48
  meshcollider:
    Code re-review and functional test run ACK 7bf6dfbb48

Tree-SHA512: 23ad18324c9e8947f0cf88a3734c2e9fb25536b2cb4d552cf5d1a4ade320fbffb73bb2d1b3a99585c11630aa7092e0fcfc2dd4fe65b91e3a54161433a5cd13cb
This commit is contained in:
Samuel Dobson 2020-09-07 11:19:46 +12:00
commit 56d47e19ed
No known key found for this signature in database
GPG key ID: D300116E1C875A3D
30 changed files with 291 additions and 270 deletions

View file

@ -31,7 +31,7 @@ static void CoinSelection(benchmark::Bench& bench)
{ {
NodeContext node; NodeContext node;
auto chain = interfaces::MakeChain(node); auto chain = interfaces::MakeChain(node);
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
wallet.SetupLegacyScriptPubKeyMan(); wallet.SetupLegacyScriptPubKeyMan();
std::vector<std::unique_ptr<CWalletTx>> wtxs; std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
@ -65,7 +65,7 @@ static void CoinSelection(benchmark::Bench& bench)
typedef std::set<CInputCoin> CoinSet; typedef std::set<CInputCoin> CoinSet;
static NodeContext testNode; static NodeContext testNode;
static auto testChain = interfaces::MakeChain(testNode); static auto testChain = interfaces::MakeChain(testNode);
static CWallet testWallet(testChain.get(), WalletLocation(), CreateDummyWalletDatabase()); static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
std::vector<std::unique_ptr<CWalletTx>> wtxn; std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp // Copied from src/wallet/test/coinselector_tests.cpp

View file

@ -26,7 +26,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
NodeContext node; NodeContext node;
std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node); std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
CWallet wallet{chain.get(), WalletLocation(), CreateMockWalletDatabase()}; CWallet wallet{chain.get(), "", CreateMockWalletDatabase()};
{ {
wallet.SetupLegacyScriptPubKeyMan(); wallet.SetupLegacyScriptPubKeyMan();
bool first_run; bool first_run;

View file

@ -514,15 +514,22 @@ public:
void setMockTime(int64_t time) override { return SetMockTime(time); } void setMockTime(int64_t time) override { return SetMockTime(time); }
//! WalletClient methods //! WalletClient methods
std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, WalletCreationStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) override std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) override
{ {
std::shared_ptr<CWallet> wallet; std::shared_ptr<CWallet> wallet;
status = CreateWallet(*m_context.chain, passphrase, wallet_creation_flags, name, true /* load_on_start */, error, warnings, wallet); DatabaseOptions options;
return MakeWallet(std::move(wallet)); DatabaseStatus status;
options.require_create = true;
options.create_flags = wallet_creation_flags;
options.create_passphrase = passphrase;
return MakeWallet(CreateWallet(*m_context.chain, name, true /* load_on_start */, options, status, error, warnings));
} }
std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) override std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
{ {
return MakeWallet(LoadWallet(*m_context.chain, WalletLocation(name), true /* load_on_start */, error, warnings)); DatabaseOptions options;
DatabaseStatus status;
options.require_existing = true;
return MakeWallet(LoadWallet(*m_context.chain, name, true /* load_on_start */, options, status, error, warnings));
} }
std::string getWalletDir() override std::string getWalletDir() override
{ {

View file

@ -29,7 +29,6 @@ class CWallet;
enum class FeeReason; enum class FeeReason;
enum class OutputType; enum class OutputType;
enum class TransactionError; enum class TransactionError;
enum class WalletCreationStatus;
enum isminetype : unsigned int; enum isminetype : unsigned int;
struct CRecipient; struct CRecipient;
struct PartiallySignedTransaction; struct PartiallySignedTransaction;
@ -311,7 +310,7 @@ class WalletClient : public ChainClient
{ {
public: public:
//! Create new wallet. //! Create new wallet.
virtual std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, WalletCreationStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0; virtual std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
//! Load existing wallet. //! Load existing wallet.
virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0; virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;

View file

@ -61,7 +61,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
{ {
TestChain100Setup test; TestChain100Setup test;
node.setContext(&test.m_node); node.setContext(&test.m_node);
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();
bool firstRun; bool firstRun;
wallet->LoadWallet(firstRun); wallet->LoadWallet(firstRun);

View file

@ -139,7 +139,7 @@ void TestGUI(interfaces::Node& node)
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
} }
node.setContext(&test.m_node); node.setContext(&test.m_node);
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
bool firstRun; bool firstRun;
wallet->LoadWallet(firstRun); wallet->LoadWallet(firstRun);
{ {

View file

@ -249,10 +249,9 @@ void CreateWalletActivity::createWallet()
} }
QTimer::singleShot(500, worker(), [this, name, flags] { QTimer::singleShot(500, worker(), [this, name, flags] {
WalletCreationStatus status; std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message);
std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, status, m_error_message, m_warning_message);
if (status == WalletCreationStatus::SUCCESS) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet)); if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
QTimer::singleShot(500, this, &CreateWalletActivity::finish); QTimer::singleShot(500, this, &CreateWalletActivity::finish);
}); });

View file

@ -52,18 +52,6 @@ bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
return memcmp(value, &rhs.value, sizeof(value)) == 0; return memcmp(value, &rhs.value, sizeof(value)) == 0;
} }
bool IsBDBWalletLoaded(const fs::path& wallet_path)
{
fs::path env_directory;
std::string database_filename;
SplitWalletPath(wallet_path, env_directory, database_filename);
LOCK(cs_db);
auto env = g_dbenvs.find(env_directory.string());
if (env == g_dbenvs.end()) return false;
auto database = env->second.lock();
return database && database->IsDatabaseLoaded(database_filename);
}
/** /**
* @param[in] wallet_path Path to wallet directory. Or (for backwards compatibility only) a path to a berkeley btree data file inside a wallet directory. * @param[in] wallet_path Path to wallet directory. Or (for backwards compatibility only) a path to a berkeley btree data file inside a wallet directory.
* @param[out] database_filename Filename of berkeley btree data file inside the wallet directory. * @param[out] database_filename Filename of berkeley btree data file inside the wallet directory.
@ -371,7 +359,6 @@ void BerkeleyDatabase::Open(const char* pszMode)
if (ret != 0) { if (ret != 0) {
throw std::runtime_error(strprintf("BerkeleyDatabase: Error %d, can't open database %s", ret, strFile)); throw std::runtime_error(strprintf("BerkeleyDatabase: Error %d, can't open database %s", ret, strFile));
} }
m_file_path = (env->Directory() / strFile).string();
// Call CheckUniqueFileid on the containing BDB environment to // Call CheckUniqueFileid on the containing BDB environment to
// avoid BDB data consistency bugs that happen when different data // avoid BDB data consistency bugs that happen when different data
@ -824,3 +811,35 @@ std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(const char* mode, boo
{ {
return MakeUnique<BerkeleyBatch>(*this, mode, flush_on_close); return MakeUnique<BerkeleyBatch>(*this, mode, flush_on_close);
} }
bool ExistsBerkeleyDatabase(const fs::path& path)
{
fs::path env_directory;
std::string data_filename;
SplitWalletPath(path, env_directory, data_filename);
return IsBerkeleyBtree(env_directory / data_filename);
}
std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
{
std::unique_ptr<BerkeleyDatabase> db;
{
LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
std::string data_filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(path, data_filename);
if (env->m_databases.count(data_filename)) {
error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", (env->Directory() / data_filename).string()));
status = DatabaseStatus::FAILED_ALREADY_LOADED;
return nullptr;
}
db = MakeUnique<BerkeleyDatabase>(std::move(env), std::move(data_filename));
}
if (options.verify && !db->Verify(error)) {
status = DatabaseStatus::FAILED_VERIFY;
return nullptr;
}
status = DatabaseStatus::SUCCESS;
return db;
}

View file

@ -63,7 +63,6 @@ public:
bool IsMock() const { return fMockDb; } bool IsMock() const { return fMockDb; }
bool IsInitialized() const { return fDbEnvInit; } bool IsInitialized() const { return fDbEnvInit; }
bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
fs::path Directory() const { return strPath; } fs::path Directory() const { return strPath; }
bool Open(bilingual_str& error); bool Open(bilingual_str& error);
@ -87,8 +86,8 @@ public:
/** Get BerkeleyEnvironment and database filename given a wallet path. */ /** Get BerkeleyEnvironment and database filename given a wallet path. */
std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename); std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
/** Return whether a BDB wallet database is currently loaded. */ /** Check format of database file */
bool IsBDBWalletLoaded(const fs::path& wallet_path); bool IsBerkeleyBtree(const fs::path& path);
class BerkeleyBatch; class BerkeleyBatch;
@ -143,7 +142,10 @@ public:
void ReloadDbEnv() override; void ReloadDbEnv() override;
/** Verifies the environment and database file */ /** Verifies the environment and database file */
bool Verify(bilingual_str& error) override; bool Verify(bilingual_str& error);
/** Return path to main database filename */
std::string Filename() override { return (env->Directory() / strFile).string(); }
/** /**
* Pointer to shared database environment. * Pointer to shared database environment.
@ -224,4 +226,10 @@ public:
std::string BerkeleyDatabaseVersion(); std::string BerkeleyDatabaseVersion();
//! Check if Berkeley database exists at specified path.
bool ExistsBerkeleyDatabase(const fs::path& path);
//! Return object giving access to Berkeley database at specified path.
std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
#endif // BITCOIN_WALLET_BDB_H #endif // BITCOIN_WALLET_BDB_H

View file

@ -23,11 +23,3 @@ void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::
database_filename = "wallet.dat"; database_filename = "wallet.dat";
} }
} }
fs::path WalletDataFilePath(const fs::path& wallet_path)
{
fs::path env_directory;
std::string database_filename;
SplitWalletPath(wallet_path, env_directory, database_filename);
return env_directory / database_filename;
}

View file

@ -9,6 +9,7 @@
#include <clientversion.h> #include <clientversion.h>
#include <fs.h> #include <fs.h>
#include <streams.h> #include <streams.h>
#include <support/allocators/secure.h>
#include <util/memory.h> #include <util/memory.h>
#include <atomic> #include <atomic>
@ -17,8 +18,6 @@
struct bilingual_str; 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); void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);
/** RAII class that provides access to a WalletDatabase */ /** RAII class that provides access to a WalletDatabase */
@ -141,16 +140,14 @@ public:
virtual void ReloadDbEnv() = 0; virtual void ReloadDbEnv() = 0;
/** Return path to main database file for logs and error messages. */
virtual std::string Filename() = 0;
std::atomic<unsigned int> nUpdateCounter; std::atomic<unsigned int> nUpdateCounter;
unsigned int nLastSeen; unsigned int nLastSeen;
unsigned int nLastFlushed; unsigned int nLastFlushed;
int64_t nLastWalletUpdate; 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 */ /** Make a DatabaseBatch connected to this database */
virtual std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) = 0; virtual std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) = 0;
}; };
@ -191,8 +188,34 @@ public:
bool PeriodicFlush() override { return true; } bool PeriodicFlush() override { return true; }
void IncrementUpdateCounter() override { ++nUpdateCounter; } void IncrementUpdateCounter() override { ++nUpdateCounter; }
void ReloadDbEnv() override {} void ReloadDbEnv() override {}
bool Verify(bilingual_str& errorStr) override { return true; } std::string Filename() override { return "dummy"; }
std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) override { return MakeUnique<DummyBatch>(); } std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) override { return MakeUnique<DummyBatch>(); }
}; };
enum class DatabaseFormat {
BERKELEY,
};
struct DatabaseOptions {
bool require_existing = false;
bool require_create = false;
uint64_t create_flags = 0;
SecureString create_passphrase;
bool verify = true;
};
enum class DatabaseStatus {
SUCCESS,
FAILED_BAD_PATH,
FAILED_BAD_FORMAT,
FAILED_ALREADY_LOADED,
FAILED_ALREADY_EXISTS,
FAILED_NOT_FOUND,
FAILED_CREATE,
FAILED_VERIFY,
FAILED_ENCRYPT,
};
std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
#endif // BITCOIN_WALLET_DB_H #endif // BITCOIN_WALLET_DB_H

View file

@ -5,6 +5,7 @@
#include <wallet/load.h> #include <wallet/load.h>
#include <fs.h>
#include <interfaces/chain.h> #include <interfaces/chain.h>
#include <scheduler.h> #include <scheduler.h>
#include <util/string.h> #include <util/string.h>
@ -44,18 +45,18 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
std::set<fs::path> wallet_paths; std::set<fs::path> wallet_paths;
for (const auto& wallet_file : wallet_files) { for (const auto& wallet_file : wallet_files) {
WalletLocation location(wallet_file); const fs::path path = fs::absolute(wallet_file, GetWalletDir());
if (!wallet_paths.insert(location.GetPath()).second) { if (!wallet_paths.insert(path).second) {
chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file)); chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
return false; return false;
} }
DatabaseOptions options;
DatabaseStatus status;
options.verify = true;
bilingual_str error_string; bilingual_str error_string;
std::vector<bilingual_str> warnings; if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
bool verify_success = CWallet::Verify(chain, location, error_string, warnings);
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
if (!verify_success) {
chain.initError(error_string); chain.initError(error_string);
return false; return false;
} }
@ -67,10 +68,14 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files) bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
{ {
try { try {
for (const std::string& walletFile : wallet_files) { for (const std::string& name : wallet_files) {
DatabaseOptions options;
DatabaseStatus status;
options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
bilingual_str error; bilingual_str error;
std::vector<bilingual_str> warnings; std::vector<bilingual_str> warnings;
std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile), error, warnings); std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(chain, name, std::move(database), options.create_flags, error, warnings) : nullptr;
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n"))); if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
if (!pwallet) { if (!pwallet) {
chain.initError(error); chain.initError(error);

View file

@ -2502,23 +2502,21 @@ static UniValue loadwallet(const JSONRPCRequest& request)
}.Check(request); }.Check(request);
WalletContext& context = EnsureWalletContext(request.context); WalletContext& context = EnsureWalletContext(request.context);
WalletLocation location(request.params[0].get_str()); const std::string name(request.params[0].get_str());
if (!location.Exists()) {
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + location.GetName() + " not found.");
} else if (fs::is_directory(location.GetPath())) {
// The given filename is a directory. Check that there's a wallet.dat file.
fs::path wallet_dat_file = location.GetPath() / "wallet.dat";
if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) {
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + location.GetName() + " does not contain a wallet.dat file.");
}
}
DatabaseOptions options;
DatabaseStatus status;
options.require_existing = true;
bilingual_str error; bilingual_str error;
std::vector<bilingual_str> warnings; std::vector<bilingual_str> warnings;
Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool()); Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool());
std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, location, load_on_start, error, warnings); std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, name, load_on_start, options, status, error, warnings);
if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error.original); if (!wallet) {
// Map bad format to not found, since bad format is returned when the
// wallet directory exists, but doesn't contain a data file.
RPCErrorCode code = status == DatabaseStatus::FAILED_NOT_FOUND || status == DatabaseStatus::FAILED_BAD_FORMAT ? RPC_WALLET_NOT_FOUND : RPC_WALLET_ERROR;
throw JSONRPCError(code, error.original);
}
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName()); obj.pushKV("name", wallet->GetName());
@ -2647,18 +2645,17 @@ static UniValue createwallet(const JSONRPCRequest& request)
warnings.emplace_back(Untranslated("Wallet is an experimental descriptor wallet")); warnings.emplace_back(Untranslated("Wallet is an experimental descriptor wallet"));
} }
DatabaseOptions options;
DatabaseStatus status;
options.require_create = true;
options.create_flags = flags;
options.create_passphrase = passphrase;
bilingual_str error; bilingual_str error;
std::shared_ptr<CWallet> wallet;
Optional<bool> load_on_start = request.params[6].isNull() ? nullopt : Optional<bool>(request.params[6].get_bool()); Optional<bool> load_on_start = request.params[6].isNull() ? nullopt : Optional<bool>(request.params[6].get_bool());
WalletCreationStatus status = CreateWallet(*context.chain, passphrase, flags, request.params[0].get_str(), load_on_start, error, warnings, wallet); std::shared_ptr<CWallet> wallet = CreateWallet(*context.chain, request.params[0].get_str(), load_on_start, options, status, error, warnings);
switch (status) { if (!wallet) {
case WalletCreationStatus::CREATION_FAILED: RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
throw JSONRPCError(RPC_WALLET_ERROR, error.original); throw JSONRPCError(code, error.original);
case WalletCreationStatus::ENCRYPTION_FAILED:
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error.original);
case WalletCreationStatus::SUCCESS:
break;
// no default case, so the compiler can warn about missing cases
} }
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);

View file

@ -23,6 +23,13 @@ static bool KeyFilter(const std::string& type)
bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings) bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
{ {
DatabaseOptions options;
DatabaseStatus status;
options.require_existing = true;
options.verify = false;
std::unique_ptr<WalletDatabase> database = MakeDatabase(file_path, options, status, error);
if (!database) return false;
std::string filename; std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename); std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
@ -123,7 +130,7 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v
} }
DbTxn* ptxn = env->TxnBegin(); DbTxn* ptxn = env->TxnBegin();
CWallet dummyWallet(nullptr, WalletLocation(), CreateDummyWalletDatabase()); CWallet dummyWallet(nullptr, "", CreateDummyWalletDatabase());
for (KeyValPair& row : salvagedData) for (KeyValPair& row : salvagedData)
{ {
/* Filter for only private key type KV pairs to be added to the salvaged wallet */ /* Filter for only private key type KV pairs to be added to the salvaged wallet */

View file

@ -29,7 +29,7 @@ typedef std::set<CInputCoin> CoinSet;
static std::vector<COutput> vCoins; static std::vector<COutput> vCoins;
static NodeContext testNode; static NodeContext testNode;
static auto testChain = interfaces::MakeChain(testNode); static auto testChain = interfaces::MakeChain(testNode);
static CWallet testWallet(testChain.get(), WalletLocation(), CreateDummyWalletDatabase()); static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
static CAmount balance = 0; static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0); CoinEligibilityFilter filter_standard(1, 6, 0);
@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Make sure that can use BnB when there are preset inputs // Make sure that can use BnB when there are preset inputs
empty_wallet(); empty_wallet();
{ {
std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase()); std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase());
bool firstRun; bool firstRun;
wallet->LoadWallet(firstRun); wallet->LoadWallet(firstRun);
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();

View file

@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK compressed // P2PK compressed
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]); scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK uncompressed // P2PK uncompressed
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey); scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH compressed // P2PKH compressed
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0])); scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH uncompressed // P2PKH uncompressed
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey)); scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
@ -103,7 +103,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH // P2SH
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2SH (invalid) // (P2PKH inside) P2SH inside P2SH (invalid)
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2WSH (invalid) // (P2PKH inside) P2SH inside P2WSH (invalid)
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH inside P2WSH (invalid) // P2WPKH inside P2WSH (invalid)
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2WSH inside P2WSH (invalid) // (P2PKH inside) P2WSH inside P2WSH (invalid)
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH compressed // P2WPKH compressed
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH uncompressed // P2WPKH uncompressed
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@ -231,7 +231,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// scriptPubKey multisig // scriptPubKey multisig
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -262,7 +262,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH multisig // P2SH multisig
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with compressed keys // P2WSH multisig with compressed keys
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -309,7 +309,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with uncompressed key // P2WSH multisig with uncompressed key
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@ -335,7 +335,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig wrapped in P2SH // P2WSH multisig wrapped in P2SH
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@ -362,7 +362,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// OP_RETURN // OP_RETURN
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -376,7 +376,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unspendable // witness unspendable
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -390,7 +390,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unknown // witness unknown
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Nonstandard // Nonstandard
{ {
CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan(); keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));

View file

@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(CanProvide)
// Set up wallet and keyman variables. // Set up wallet and keyman variables.
NodeContext node; NodeContext node;
std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node); std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan(); LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan();
// Make a 1 of 2 multisig script // Make a 1 of 2 multisig script

View file

@ -6,7 +6,7 @@
WalletTestingSetup::WalletTestingSetup(const std::string& chainName) WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName), : TestingSetup(chainName),
m_wallet(m_chain.get(), WalletLocation(), CreateMockWalletDatabase()) m_wallet(m_chain.get(), "", CreateMockWalletDatabase())
{ {
bool fFirstRun; bool fFirstRun;
m_wallet.LoadWallet(fFirstRun); m_wallet.LoadWallet(fFirstRun);

View file

@ -37,9 +37,12 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
static std::shared_ptr<CWallet> TestLoadWallet(interfaces::Chain& chain) static std::shared_ptr<CWallet> TestLoadWallet(interfaces::Chain& chain)
{ {
DatabaseOptions options;
DatabaseStatus status;
bilingual_str error; bilingual_str error;
std::vector<bilingual_str> warnings; std::vector<bilingual_str> warnings;
auto wallet = CWallet::CreateWalletFromFile(chain, WalletLocation(""), error, warnings); auto database = MakeWalletDatabase("", options, status, error);
auto wallet = CWallet::Create(chain, "", std::move(database), options.create_flags, error, warnings);
wallet->postInitProcess(); wallet->postInitProcess();
return wallet; return wallet;
} }
@ -85,7 +88,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions fails to read an unknown start block. // Verify ScanForWalletTransactions fails to read an unknown start block.
{ {
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@ -104,7 +107,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions picks up transactions in both the old // Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files. // and new block files.
{ {
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@ -130,7 +133,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block // Verify ScanForWalletTransactions only picks transactions in the new block
// file. // file.
{ {
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@ -155,7 +158,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions scans no blocks. // Verify ScanForWalletTransactions scans no blocks.
{ {
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{ {
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@ -194,7 +197,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is // before the missing block, and success for a key whose creation time is
// after. // after.
{ {
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash())); WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
AddWallet(wallet); AddWallet(wallet);
@ -259,7 +262,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Import key into wallet and call dumpwallet to create backup file. // Import key into wallet and call dumpwallet to create backup file.
{ {
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
{ {
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan(); auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore); LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
@ -281,7 +284,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned. // were scanned, and no prior blocks were scanned.
{ {
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();
@ -317,7 +320,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
NodeContext node; NodeContext node;
auto chain = interfaces::MakeChain(node); auto chain = interfaces::MakeChain(node);
CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan(); auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
CWalletTx wtx(&wallet, m_coinbase_txns.back()); CWalletTx wtx(&wallet, m_coinbase_txns.back());
@ -492,7 +495,7 @@ public:
ListCoinsTestingSetup() ListCoinsTestingSetup()
{ {
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase()); wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase());
{ {
LOCK2(wallet->cs_wallet, ::cs_main); LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@ -610,7 +613,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{ {
NodeContext node; NodeContext node;
auto chain = interfaces::MakeChain(node); auto chain = interfaces::MakeChain(node);
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase()); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();
wallet->SetMinVersion(FEATURE_LATEST); wallet->SetMinVersion(FEATURE_LATEST);
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS); wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);

View file

@ -200,15 +200,16 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
} }
namespace { namespace {
std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const WalletLocation& location, Optional<bool> load_on_start, bilingual_str& error, std::vector<bilingual_str>& warnings) std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{ {
try { try {
if (!CWallet::Verify(chain, location, error, warnings)) { std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
if (!database) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error; error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return nullptr; return nullptr;
} }
std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings); std::shared_ptr<CWallet> wallet = CWallet::Create(chain, name, std::move(database), options.create_flags, error, warnings);
if (!wallet) { if (!wallet) {
error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error; error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
return nullptr; return nullptr;
@ -217,7 +218,7 @@ std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const Wall
wallet->postInitProcess(); wallet->postInitProcess();
// Write the wallet setting // Write the wallet setting
UpdateWalletSetting(chain, location.GetName(), load_on_start, warnings); UpdateWalletSetting(chain, name, load_on_start, warnings);
return wallet; return wallet;
} catch (const std::runtime_error& e) { } catch (const std::runtime_error& e) {
@ -227,20 +228,23 @@ std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const Wall
} }
} // namespace } // namespace
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, Optional<bool> load_on_start, bilingual_str& error, std::vector<bilingual_str>& warnings) std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{ {
auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(location.GetName())); auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name));
if (!result.second) { if (!result.second) {
error = Untranslated("Wallet already being loading."); error = Untranslated("Wallet already being loading.");
return nullptr; return nullptr;
} }
auto wallet = LoadWalletInternal(chain, location, load_on_start, error, warnings); auto wallet = LoadWalletInternal(chain, name, load_on_start, options, status, error, warnings);
WITH_LOCK(g_loading_wallet_mutex, g_loading_wallet_set.erase(result.first)); WITH_LOCK(g_loading_wallet_mutex, g_loading_wallet_set.erase(result.first));
return wallet; return wallet;
} }
WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, Optional<bool> load_on_start, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result) std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{ {
uint64_t wallet_creation_flags = options.create_flags;
const SecureString& passphrase = options.create_passphrase;
// Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted // Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET); bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET);
@ -249,43 +253,42 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET; wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET;
} }
// Check the wallet file location
WalletLocation location(name);
if (location.Exists()) {
error = strprintf(Untranslated("Wallet %s already exists."), location.GetName());
return WalletCreationStatus::CREATION_FAILED;
}
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name. // Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
if (!CWallet::Verify(chain, location, error, warnings)) { std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
if (!database) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error; error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return WalletCreationStatus::CREATION_FAILED; status = DatabaseStatus::FAILED_VERIFY;
return nullptr;
} }
// Do not allow a passphrase when private keys are disabled // Do not allow a passphrase when private keys are disabled
if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled."); error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.");
return WalletCreationStatus::CREATION_FAILED; status = DatabaseStatus::FAILED_CREATE;
return nullptr;
} }
// Make the wallet // Make the wallet
std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings, wallet_creation_flags); std::shared_ptr<CWallet> wallet = CWallet::Create(chain, name, std::move(database), wallet_creation_flags, error, warnings);
if (!wallet) { if (!wallet) {
error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error; error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
return WalletCreationStatus::CREATION_FAILED; status = DatabaseStatus::FAILED_CREATE;
return nullptr;
} }
// Encrypt the wallet // Encrypt the wallet
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (!wallet->EncryptWallet(passphrase)) { if (!wallet->EncryptWallet(passphrase)) {
error = Untranslated("Error: Wallet created but failed to encrypt."); error = Untranslated("Error: Wallet created but failed to encrypt.");
return WalletCreationStatus::ENCRYPTION_FAILED; status = DatabaseStatus::FAILED_ENCRYPT;
return nullptr;
} }
if (!create_blank) { if (!create_blank) {
// Unlock the wallet // Unlock the wallet
if (!wallet->Unlock(passphrase)) { if (!wallet->Unlock(passphrase)) {
error = Untranslated("Error: Wallet was encrypted but could not be unlocked"); error = Untranslated("Error: Wallet was encrypted but could not be unlocked");
return WalletCreationStatus::ENCRYPTION_FAILED; status = DatabaseStatus::FAILED_ENCRYPT;
return nullptr;
} }
// Set a seed for the wallet // Set a seed for the wallet
@ -297,7 +300,8 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
for (auto spk_man : wallet->GetActiveScriptPubKeyMans()) { for (auto spk_man : wallet->GetActiveScriptPubKeyMans()) {
if (!spk_man->SetupGeneration()) { if (!spk_man->SetupGeneration()) {
error = Untranslated("Unable to generate initial keys"); error = Untranslated("Unable to generate initial keys");
return WalletCreationStatus::CREATION_FAILED; status = DatabaseStatus::FAILED_CREATE;
return nullptr;
} }
} }
} }
@ -309,12 +313,12 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
} }
AddWallet(wallet); AddWallet(wallet);
wallet->postInitProcess(); wallet->postInitProcess();
result = wallet;
// Write the wallet settings // Write the wallet settings
UpdateWalletSetting(chain, name, load_on_start, warnings); UpdateWalletSetting(chain, name, load_on_start, warnings);
return WalletCreationStatus::SUCCESS; status = DatabaseStatus::SUCCESS;
return wallet;
} }
const uint256 CWalletTx::ABANDON_HASH(UINT256_ONE()); const uint256 CWalletTx::ABANDON_HASH(UINT256_ONE());
@ -3770,7 +3774,7 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values; return values;
} }
bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings) std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string)
{ {
// Do some checking on wallet path. It should be either a: // Do some checking on wallet path. It should be either a:
// //
@ -3778,40 +3782,25 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
// 2. Path to an existing directory. // 2. Path to an existing directory.
// 3. Path to a symlink to a directory. // 3. Path to a symlink to a directory.
// 4. For backwards compatibility, the name of a data file in -walletdir. // 4. For backwards compatibility, the name of a data file in -walletdir.
LOCK(cs_wallets); const fs::path& wallet_path = fs::absolute(name, GetWalletDir());
const fs::path& wallet_path = location.GetPath();
fs::file_type path_type = fs::symlink_status(wallet_path).type(); fs::file_type path_type = fs::symlink_status(wallet_path).type();
if (!(path_type == fs::file_not_found || path_type == fs::directory_file || if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
(path_type == fs::symlink_file && fs::is_directory(wallet_path)) || (path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
(path_type == fs::regular_file && fs::path(location.GetName()).filename() == location.GetName()))) { (path_type == fs::regular_file && fs::path(name).filename() == name))) {
error_string = Untranslated(strprintf( error_string = Untranslated(strprintf(
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and " "Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
"database/log.?????????? files can be stored, a location where such a directory could be created, " "database/log.?????????? files can be stored, a location where such a directory could be created, "
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)", "or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
location.GetName(), GetWalletDir())); name, GetWalletDir()));
return false; status = DatabaseStatus::FAILED_BAD_PATH;
} return nullptr;
// Make sure that the wallet path doesn't clash with an existing wallet path
if (IsWalletLoaded(wallet_path)) {
error_string = Untranslated(strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName()));
return false;
}
// Keep same database environment instance across Verify/Recover calls below.
std::unique_ptr<WalletDatabase> database = CreateWalletDatabase(wallet_path);
try {
return database->Verify(error_string);
} catch (const fs::filesystem_error& e) {
error_string = Untranslated(strprintf("Error loading wallet %s. %s", location.GetName(), fsbridge::get_filesystem_error_message(e)));
return false;
} }
return MakeDatabase(wallet_path, options, status, error_string);
} }
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags) std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
{ {
const std::string walletFile = WalletDataFilePath(location.GetPath()).string(); const std::string& walletFile = database->Filename();
chain.initMessage(_("Loading wallet...").translated); chain.initMessage(_("Loading wallet...").translated);
@ -3819,7 +3808,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
bool fFirstRun = true; bool fFirstRun = true;
// TODO: Can't use std::make_shared because we need a custom deleter but // TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared. // should be possible to use std::allocate_shared.
std::shared_ptr<CWallet> walletInstance(new CWallet(&chain, location, CreateWalletDatabase(location.GetPath())), ReleaseWallet); std::shared_ptr<CWallet> walletInstance(new CWallet(&chain, name, std::move(database)), ReleaseWallet);
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK) { if (nLoadWalletRet != DBErrors::LOAD_OK) {
if (nLoadWalletRet == DBErrors::CORRUPT) { if (nLoadWalletRet == DBErrors::CORRUPT) {

View file

@ -54,16 +54,10 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on
bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start); bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start);
std::vector<std::shared_ptr<CWallet>> GetWallets(); std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name); std::shared_ptr<CWallet> GetWallet(const std::string& name);
std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, Optional<bool> load_on_start, bilingual_str& error, std::vector<bilingual_str>& warnings); std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet); std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet);
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
enum class WalletCreationStatus {
SUCCESS,
CREATION_FAILED,
ENCRYPTION_FAILED
};
WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, Optional<bool> load_on_start, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result);
//! -paytxfee default //! -paytxfee default
constexpr CAmount DEFAULT_PAY_TX_FEE = 0; constexpr CAmount DEFAULT_PAY_TX_FEE = 0;
@ -700,8 +694,8 @@ private:
/** Interface for accessing chain state. */ /** Interface for accessing chain state. */
interfaces::Chain* m_chain; interfaces::Chain* m_chain;
/** Wallet location which includes wallet name (see WalletLocation). */ /** Wallet name: relative directory name or "" for default wallet. */
WalletLocation m_location; std::string m_name;
/** Internal database handle. */ /** Internal database handle. */
std::unique_ptr<WalletDatabase> database; std::unique_ptr<WalletDatabase> database;
@ -755,20 +749,18 @@ public:
bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
const WalletLocation& GetLocation() const { return m_location; }
/** Get a name for this wallet for logging/debugging purposes. /** Get a name for this wallet for logging/debugging purposes.
*/ */
const std::string& GetName() const { return m_location.GetName(); } const std::string& GetName() const { return m_name; }
typedef std::map<unsigned int, CMasterKey> MasterKeyMap; typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys; MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID = 0; unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */ /** Construct wallet with specified name and database implementation. */
CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database) CWallet(interfaces::Chain* chain, const std::string& name, std::unique_ptr<WalletDatabase> database)
: m_chain(chain), : m_chain(chain),
m_location(location), m_name(name),
database(std::move(database)) database(std::move(database))
{ {
} }
@ -1153,11 +1145,8 @@ public:
/** Mark a transaction as replaced by another transaction (e.g., BIP 125). */ /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
bool MarkReplaced(const uint256& originalHash, const uint256& newHash); bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
//! Verify wallet naming and perform salvage on the wallet if required
static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
static std::shared_ptr<CWallet> CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags = 0); static std::shared_ptr<CWallet> Create(interfaces::Chain& chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings);
/** /**
* Wallet post-init setup * Wallet post-init setup

View file

@ -13,6 +13,8 @@
#include <util/bip32.h> #include <util/bip32.h>
#include <util/system.h> #include <util/system.h>
#include <util/time.h> #include <util/time.h>
#include <util/translation.h>
#include <wallet/bdb.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <atomic> #include <atomic>
@ -993,16 +995,41 @@ bool WalletBatch::TxnAbort()
return m_batch->TxnAbort(); return m_batch->TxnAbort();
} }
bool IsWalletLoaded(const fs::path& wallet_path) std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
{ {
return IsBDBWalletLoaded(wallet_path); bool exists;
} try {
exists = fs::symlink_status(path).type() != fs::file_not_found;
} catch (const fs::filesystem_error& e) {
error = Untranslated(strprintf("Failed to access database path '%s': %s", path.string(), fsbridge::get_filesystem_error_message(e)));
status = DatabaseStatus::FAILED_BAD_PATH;
return nullptr;
}
/** Return object for accessing database at specified path. */ Optional<DatabaseFormat> format;
std::unique_ptr<WalletDatabase> CreateWalletDatabase(const fs::path& path) if (exists) {
{ if (ExistsBerkeleyDatabase(path)) {
std::string filename; format = DatabaseFormat::BERKELEY;
return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename)); }
} else if (options.require_existing) {
error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", path.string()));
status = DatabaseStatus::FAILED_NOT_FOUND;
return nullptr;
}
if (!format && options.require_existing) {
error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", path.string()));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}
if (format && options.require_create) {
error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", path.string()));
status = DatabaseStatus::FAILED_ALREADY_EXISTS;
return nullptr;
}
return MakeBerkeleyDatabase(path, options, status, error);
} }
/** Return object for accessing dummy database with no read/write capabilities. */ /** Return object for accessing dummy database with no read/write capabilities. */

View file

@ -285,12 +285,6 @@ using KeyFilterFn = std::function<bool(const std::string&)>;
//! Unserialize a given Key-Value pair and load it into the wallet //! Unserialize a given Key-Value pair and load it into the wallet
bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr); bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr);
/** Return whether a wallet database is currently loaded. */
bool IsWalletLoaded(const fs::path& wallet_path);
/** Return object for accessing database at specified path. */
std::unique_ptr<WalletDatabase> CreateWalletDatabase(const fs::path& path);
/** Return object for accessing dummy database with no read/write capabilities. */ /** Return object for accessing dummy database with no read/write capabilities. */
std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase(); std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase();

View file

@ -21,21 +21,9 @@ static void WalletToolReleaseWallet(CWallet* wallet)
delete wallet; delete wallet;
} }
static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path) static void WalletCreate(CWallet* wallet_instance)
{ {
if (fs::exists(path)) {
tfm::format(std::cerr, "Error: File exists already\n");
return nullptr;
}
// dummy chain interface
std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), CreateWalletDatabase(path)), WalletToolReleaseWallet);
LOCK(wallet_instance->cs_wallet); LOCK(wallet_instance->cs_wallet);
bool first_run = true;
DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run);
if (load_wallet_ret != DBErrors::LOAD_OK) {
tfm::format(std::cerr, "Error creating %s", name);
return nullptr;
}
wallet_instance->SetMinVersion(FEATURE_HD_SPLIT); wallet_instance->SetMinVersion(FEATURE_HD_SPLIT);
@ -46,18 +34,26 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
tfm::format(std::cout, "Topping up keypool...\n"); tfm::format(std::cout, "Topping up keypool...\n");
wallet_instance->TopUpKeyPool(); wallet_instance->TopUpKeyPool();
return wallet_instance;
} }
static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path) static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, bool create)
{ {
if (!fs::exists(path)) { DatabaseOptions options;
tfm::format(std::cerr, "Error: Wallet files does not exist\n"); DatabaseStatus status;
if (create) {
options.require_create = true;
} else {
options.require_existing = true;
}
bilingual_str error;
std::unique_ptr<WalletDatabase> database = MakeDatabase(path, options, status, error);
if (!database) {
tfm::format(std::cerr, "%s\n", error.original);
return nullptr; return nullptr;
} }
// dummy chain interface // dummy chain interface
std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), CreateWalletDatabase(path)), WalletToolReleaseWallet); std::shared_ptr<CWallet> wallet_instance{new CWallet(nullptr /* chain */, name, std::move(database)), WalletToolReleaseWallet};
DBErrors load_wallet_ret; DBErrors load_wallet_ret;
try { try {
bool first_run; bool first_run;
@ -89,6 +85,8 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
} }
} }
if (create) WalletCreate(wallet_instance.get());
return wallet_instance; return wallet_instance;
} }
@ -109,19 +107,14 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
fs::path path = fs::absolute(name, GetWalletDir()); fs::path path = fs::absolute(name, GetWalletDir());
if (command == "create") { if (command == "create") {
std::shared_ptr<CWallet> wallet_instance = CreateWallet(name, path); std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ true);
if (wallet_instance) { if (wallet_instance) {
WalletShowInfo(wallet_instance.get()); WalletShowInfo(wallet_instance.get());
wallet_instance->Close(); wallet_instance->Close();
} }
} else if (command == "info" || command == "salvage") { } else if (command == "info" || command == "salvage") {
if (!fs::exists(path)) {
tfm::format(std::cerr, "Error: no wallet file at %s\n", name);
return false;
}
if (command == "info") { if (command == "info") {
std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path); std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ false);
if (!wallet_instance) return false; if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get()); WalletShowInfo(wallet_instance.get());
wallet_instance->Close(); wallet_instance->Close();

View file

@ -9,8 +9,6 @@
namespace WalletTool { namespace WalletTool {
std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path);
std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path);
void WalletShowInfo(CWallet* wallet_instance); void WalletShowInfo(CWallet* wallet_instance);
bool ExecuteWalletToolFunc(const std::string& command, const std::string& file); bool ExecuteWalletToolFunc(const std::string& command, const std::string& file);

View file

@ -29,7 +29,7 @@ fs::path GetWalletDir()
return path; return path;
} }
static bool IsBerkeleyBtree(const fs::path& path) bool IsBerkeleyBtree(const fs::path& path)
{ {
if (!fs::exists(path)) return false; if (!fs::exists(path)) return false;
@ -91,19 +91,3 @@ std::vector<fs::path> ListWalletDir()
return paths; return paths;
} }
WalletLocation::WalletLocation(const std::string& name)
: m_name(name)
, m_path(fs::absolute(name, GetWalletDir()))
{
}
bool WalletLocation::Exists() const
{
fs::path path = m_path;
// For the default wallet, check specifically for the wallet.dat file
if (m_name.empty()) {
path = fs::absolute("wallet.dat", m_path);
}
return fs::symlink_status(path).type() != fs::file_not_found;
}

View file

@ -67,26 +67,6 @@ fs::path GetWalletDir();
//! Get wallets in wallet directory. //! Get wallets in wallet directory.
std::vector<fs::path> ListWalletDir(); std::vector<fs::path> ListWalletDir();
//! The WalletLocation class provides wallet information.
class WalletLocation final
{
std::string m_name;
fs::path m_path;
public:
explicit WalletLocation() {}
explicit WalletLocation(const std::string& name);
//! Get wallet name.
const std::string& GetName() const { return m_name; }
//! Get wallet absolute path.
const fs::path& GetPath() const { return m_path; }
//! Return whether the wallet exists.
bool Exists() const;
};
/** Descriptor with some wallet metadata */ /** Descriptor with some wallet metadata */
class WalletDescriptor class WalletDescriptor
{ {

View file

@ -129,7 +129,8 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
try: try:
node1.loadwallet('wmulti') node1.loadwallet('wmulti')
except JSONRPCException as e: except JSONRPCException as e:
if e.error['code'] == -18 and 'Wallet wmulti not found' in e.error['message']: path = os.path.join(self.options.tmpdir, "node1", "regtest", "wallets", "wmulti")
if e.error['code'] == -18 and "Wallet file verification failed. Failed to load database path '{}'. Path does not exist.".format(path) in e.error['message']:
node1.createwallet(wallet_name='wmulti', disable_private_keys=True) node1.createwallet(wallet_name='wmulti', disable_private_keys=True)
else: else:
raise raise

View file

@ -70,12 +70,14 @@ class ToolWalletTest(BitcoinTestFramework):
self.assert_raises_tool_error('Invalid command: help', 'help') self.assert_raises_tool_error('Invalid command: help', 'help')
self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create') self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create')
self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo') self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo')
locked_dir = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets")
self.assert_raises_tool_error( self.assert_raises_tool_error(
'Error loading wallet.dat. Is wallet being used by another process?', 'Error initializing wallet database environment "{}"!'.format(locked_dir),
'-wallet=wallet.dat', '-wallet=wallet.dat',
'info', 'info',
) )
self.assert_raises_tool_error('Error: no wallet file at nonexistent.dat', '-wallet=nonexistent.dat', 'info') path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "nonexistent.dat")
self.assert_raises_tool_error("Failed to load database path '{}'. Path does not exist.".format(path), '-wallet=nonexistent.dat', 'info')
def test_tool_wallet_info(self): def test_tool_wallet_info(self):
# Stop the node to close the wallet to call the info command. # Stop the node to close the wallet to call the info command.

View file

@ -244,13 +244,16 @@ class MultiWalletTest(BitcoinTestFramework):
assert_equal(set(self.nodes[0].listwallets()), set(wallet_names)) assert_equal(set(self.nodes[0].listwallets()), set(wallet_names))
# Fail to load if wallet doesn't exist # Fail to load if wallet doesn't exist
assert_raises_rpc_error(-18, 'Wallet wallets not found.', self.nodes[0].loadwallet, 'wallets') path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "wallets")
assert_raises_rpc_error(-18, "Wallet file verification failed. Failed to load database path '{}'. Path does not exist.".format(path), self.nodes[0].loadwallet, 'wallets')
# Fail to load duplicate wallets # Fail to load duplicate wallets
assert_raises_rpc_error(-4, 'Wallet file verification failed. Error loading wallet w1. Duplicate -wallet filename specified.', self.nodes[0].loadwallet, wallet_names[0]) path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "w1", "wallet.dat")
assert_raises_rpc_error(-4, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, wallet_names[0])
# Fail to load duplicate wallets by different ways (directory and filepath) # Fail to load duplicate wallets by different ways (directory and filepath)
assert_raises_rpc_error(-4, "Wallet file verification failed. Error loading wallet wallet.dat. Duplicate -wallet filename specified.", self.nodes[0].loadwallet, 'wallet.dat') path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "wallet.dat")
assert_raises_rpc_error(-4, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, 'wallet.dat')
# Fail to load if one wallet is a copy of another # Fail to load if one wallet is a copy of another
assert_raises_rpc_error(-4, "BerkeleyDatabase: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy') assert_raises_rpc_error(-4, "BerkeleyDatabase: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy')
@ -264,12 +267,14 @@ class MultiWalletTest(BitcoinTestFramework):
# Fail to load if a directory is specified that doesn't contain a wallet # Fail to load if a directory is specified that doesn't contain a wallet
os.mkdir(wallet_dir('empty_wallet_dir')) os.mkdir(wallet_dir('empty_wallet_dir'))
assert_raises_rpc_error(-18, "Directory empty_wallet_dir does not contain a wallet.dat file", self.nodes[0].loadwallet, 'empty_wallet_dir') path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "empty_wallet_dir")
assert_raises_rpc_error(-18, "Wallet file verification failed. Failed to load database path '{}'. Data is not in recognized format.".format(path), self.nodes[0].loadwallet, 'empty_wallet_dir')
self.log.info("Test dynamic wallet creation.") self.log.info("Test dynamic wallet creation.")
# Fail to create a wallet if it already exists. # Fail to create a wallet if it already exists.
assert_raises_rpc_error(-4, "Wallet w2 already exists.", self.nodes[0].createwallet, 'w2') path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "w2")
assert_raises_rpc_error(-4, "Failed to create database path '{}'. Database already exists.".format(path), self.nodes[0].createwallet, 'w2')
# Successfully create a wallet with a new name # Successfully create a wallet with a new name
loadwallet_name = self.nodes[0].createwallet('w9') loadwallet_name = self.nodes[0].createwallet('w9')