aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp311
1 files changed, 155 insertions, 156 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index c132a4be42..73e11a5b52 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -33,6 +33,8 @@
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
+#include <univalue.h>
+
#include <algorithm>
#include <assert.h>
@@ -54,6 +56,42 @@ static RecursiveMutex cs_wallets;
static std::vector<std::shared_ptr<CWallet>> vpwallets GUARDED_BY(cs_wallets);
static std::list<LoadWalletFn> g_load_wallet_fns GUARDED_BY(cs_wallets);
+bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
+{
+ util::SettingsValue setting_value = chain.getRwSetting("wallet");
+ if (!setting_value.isArray()) setting_value.setArray();
+ for (const util::SettingsValue& value : setting_value.getValues()) {
+ if (value.isStr() && value.get_str() == wallet_name) return true;
+ }
+ setting_value.push_back(wallet_name);
+ return chain.updateRwSetting("wallet", setting_value);
+}
+
+bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
+{
+ util::SettingsValue setting_value = chain.getRwSetting("wallet");
+ if (!setting_value.isArray()) return true;
+ util::SettingsValue new_value(util::SettingsValue::VARR);
+ for (const util::SettingsValue& value : setting_value.getValues()) {
+ if (!value.isStr() || value.get_str() != wallet_name) new_value.push_back(value);
+ }
+ if (new_value.size() == setting_value.size()) return true;
+ return chain.updateRwSetting("wallet", new_value);
+}
+
+static void UpdateWalletSetting(interfaces::Chain& chain,
+ const std::string& wallet_name,
+ Optional<bool> load_on_startup,
+ std::vector<bilingual_str>& warnings)
+{
+ if (load_on_startup == nullopt) return;
+ if (load_on_startup.get() && !AddWalletSetting(chain, wallet_name)) {
+ warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup."));
+ } else if (!load_on_startup.get() && !RemoveWalletSetting(chain, wallet_name)) {
+ warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may still be loaded next node startup."));
+ }
+}
+
bool AddWallet(const std::shared_ptr<CWallet>& wallet)
{
LOCK(cs_wallets);
@@ -65,18 +103,32 @@ bool AddWallet(const std::shared_ptr<CWallet>& wallet)
return true;
}
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet)
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings)
{
assert(wallet);
+
+ interfaces::Chain& chain = wallet->chain();
+ std::string name = wallet->GetName();
+
// Unregister with the validation interface which also drops shared ponters.
wallet->m_chain_notifications_handler.reset();
LOCK(cs_wallets);
std::vector<std::shared_ptr<CWallet>>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet);
if (i == vpwallets.end()) return false;
vpwallets.erase(i);
+
+ // Write the wallet setting
+ UpdateWalletSetting(chain, name, load_on_start, warnings);
+
return true;
}
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start)
+{
+ std::vector<bilingual_str> warnings;
+ return RemoveWallet(wallet, load_on_start, warnings);
+}
+
std::vector<std::shared_ptr<CWallet>> GetWallets()
{
LOCK(cs_wallets);
@@ -148,48 +200,54 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
}
namespace {
-std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const WalletLocation& location, 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 {
- 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;
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) {
error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
+ status = DatabaseStatus::FAILED_LOAD;
return nullptr;
}
AddWallet(wallet);
wallet->postInitProcess();
+
+ // Write the wallet setting
+ UpdateWalletSetting(chain, name, load_on_start, warnings);
+
return wallet;
} catch (const std::runtime_error& e) {
error = Untranslated(e.what());
+ status = DatabaseStatus::FAILED_LOAD;
return nullptr;
}
}
} // namespace
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, 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) {
error = Untranslated("Wallet already being loading.");
+ status = DatabaseStatus::FAILED_LOAD;
return nullptr;
}
- auto wallet = LoadWalletInternal(chain, location, 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));
return wallet;
}
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, 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)
{
- return LoadWallet(chain, WalletLocation(name), error, warnings);
-}
+ uint64_t wallet_creation_flags = options.create_flags;
+ const SecureString& passphrase = options.create_passphrase;
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result)
-{
// 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);
@@ -198,43 +256,42 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
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.
- 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;
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_VERIFY;
+ return nullptr;
}
// Do not allow a passphrase when private keys are disabled
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.");
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
}
// 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) {
error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
}
// Encrypt the wallet
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (!wallet->EncryptWallet(passphrase)) {
error = Untranslated("Error: Wallet created but failed to encrypt.");
- return WalletCreationStatus::ENCRYPTION_FAILED;
+ status = DatabaseStatus::FAILED_ENCRYPT;
+ return nullptr;
}
if (!create_blank) {
// Unlock the wallet
if (!wallet->Unlock(passphrase)) {
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
@@ -246,7 +303,8 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
for (auto spk_man : wallet->GetActiveScriptPubKeyMans()) {
if (!spk_man->SetupGeneration()) {
error = Untranslated("Unable to generate initial keys");
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
}
}
}
@@ -258,8 +316,12 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
}
AddWallet(wallet);
wallet->postInitProcess();
- result = wallet;
- return WalletCreationStatus::SUCCESS;
+
+ // Write the wallet settings
+ UpdateWalletSetting(chain, name, load_on_start, warnings);
+
+ status = DatabaseStatus::SUCCESS;
+ return wallet;
}
const uint256 CWalletTx::ABANDON_HASH(UINT256_ONE());
@@ -276,7 +338,7 @@ std::string COutput::ToString() const
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
- LOCK(cs_wallet);
+ AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
if (it == mapWallet.end())
return nullptr;
@@ -1210,15 +1272,13 @@ void CWallet::BlockUntilSyncedToCurrentChain() const {
isminetype CWallet::IsMine(const CTxIn &txin) const
{
+ AssertLockHeld(cs_wallet);
+ std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
+ if (mi != mapWallet.end())
{
- LOCK(cs_wallet);
- std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
- if (mi != mapWallet.end())
- {
- const CWalletTx& prev = (*mi).second;
- if (txin.prevout.n < prev.tx->vout.size())
- return IsMine(prev.tx->vout[txin.prevout.n]);
- }
+ const CWalletTx& prev = (*mi).second;
+ if (txin.prevout.n < prev.tx->vout.size())
+ return IsMine(prev.tx->vout[txin.prevout.n]);
}
return ISMINE_NO;
}
@@ -1243,16 +1303,19 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
isminetype CWallet::IsMine(const CTxOut& txout) const
{
+ AssertLockHeld(cs_wallet);
return IsMine(txout.scriptPubKey);
}
isminetype CWallet::IsMine(const CTxDestination& dest) const
{
+ AssertLockHeld(cs_wallet);
return IsMine(GetScriptForDestination(dest));
}
isminetype CWallet::IsMine(const CScript& script) const
{
+ AssertLockHeld(cs_wallet);
isminetype result = ISMINE_NO;
for (const auto& spk_man_pair : m_spk_managers) {
result = std::max(result, spk_man_pair.second->IsMine(script));
@@ -1264,6 +1327,7 @@ CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) cons
{
if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range");
+ LOCK(cs_wallet);
return ((IsMine(txout) & filter) ? txout.nValue : 0);
}
@@ -1281,13 +1345,12 @@ bool CWallet::IsChange(const CScript& script) const
// a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change).
+ AssertLockHeld(cs_wallet);
if (IsMine(script))
{
CTxDestination address;
if (!ExtractDestination(script, address))
return true;
-
- LOCK(cs_wallet);
if (!FindAddressBookEntry(address)) {
return true;
}
@@ -1297,6 +1360,7 @@ bool CWallet::IsChange(const CScript& script) const
CAmount CWallet::GetChange(const CTxOut& txout) const
{
+ AssertLockHeld(cs_wallet);
if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range");
return (IsChange(txout) ? txout.nValue : 0);
@@ -1304,6 +1368,7 @@ CAmount CWallet::GetChange(const CTxOut& txout) const
bool CWallet::IsMine(const CTransaction& tx) const
{
+ AssertLockHeld(cs_wallet);
for (const CTxOut& txout : tx.vout)
if (IsMine(txout))
return true;
@@ -1362,6 +1427,7 @@ CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) c
CAmount CWallet::GetChange(const CTransaction& tx) const
{
+ LOCK(cs_wallet);
CAmount nChange = 0;
for (const CTxOut& txout : tx.vout)
{
@@ -1597,6 +1663,7 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
nFee = nDebit - nValueOut;
}
+ LOCK(pwallet->cs_wallet);
// Sent/received.
for (unsigned int i = 0; i < tx->vout.size(); ++i)
{
@@ -1965,36 +2032,38 @@ bool CWalletTx::InMempool() const
bool CWalletTx::IsTrusted() const
{
- std::set<uint256> s;
- return IsTrusted(s);
+ std::set<uint256> trusted_parents;
+ LOCK(pwallet->cs_wallet);
+ return pwallet->IsTrusted(*this, trusted_parents);
}
-bool CWalletTx::IsTrusted(std::set<uint256>& trusted_parents) const
+bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const
{
+ AssertLockHeld(cs_wallet);
// Quick answer in most cases
- if (!pwallet->chain().checkFinalTx(*tx)) return false;
- int nDepth = GetDepthInMainChain();
+ if (!chain().checkFinalTx(*wtx.tx)) return false;
+ int nDepth = wtx.GetDepthInMainChain();
if (nDepth >= 1) return true;
if (nDepth < 0) return false;
// using wtx's cached debit
- if (!pwallet->m_spend_zero_conf_change || !IsFromMe(ISMINE_ALL)) return false;
+ if (!m_spend_zero_conf_change || !wtx.IsFromMe(ISMINE_ALL)) return false;
// Don't trust unconfirmed transactions from us unless they are in the mempool.
- if (!InMempool()) return false;
+ if (!wtx.InMempool()) return false;
// Trusted if all inputs are from us and are in the mempool:
- for (const CTxIn& txin : tx->vin)
+ for (const CTxIn& txin : wtx.tx->vin)
{
// Transactions not sent by us: not trusted
- const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash);
+ const CWalletTx* parent = GetWalletTx(txin.prevout.hash);
if (parent == nullptr) return false;
const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
// Check that this specific input being spent is trusted
- if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) return false;
+ if (IsMine(parentOut) != ISMINE_SPENDABLE) return false;
// If we've already trusted this parent, continue
if (trusted_parents.count(parent->GetHash())) continue;
// Recurse to check that the parent is also trusted
- if (!parent->IsTrusted(trusted_parents)) return false;
+ if (!IsTrusted(*parent, trusted_parents)) return false;
trusted_parents.insert(parent->GetHash());
}
return true;
@@ -2080,7 +2149,7 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) cons
for (const auto& entry : mapWallet)
{
const CWalletTx& wtx = entry.second;
- const bool is_trusted{wtx.IsTrusted(trusted_parents)};
+ const bool is_trusted{IsTrusted(wtx, trusted_parents)};
const int tx_depth{wtx.GetDepthInMainChain()};
const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
@@ -2148,7 +2217,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
if (nDepth == 0 && !wtx.InMempool())
continue;
- bool safeTx = wtx.IsTrusted(trusted_parents);
+ bool safeTx = IsTrusted(wtx, trusted_parents);
// We should not consider coins from transactions that are replacing
// other transactions.
@@ -2284,6 +2353,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const
{
+ AssertLockHeld(cs_wallet);
const CTransaction* ptx = &tx;
int n = output;
while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) {
@@ -2580,10 +2650,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
if (!coinControl.IsSelected(txin.prevout)) {
tx.vin.push_back(txin);
- if (lockUnspents) {
- LockCoin(txin.prevout);
- }
}
+ if (lockUnspents) {
+ LockCoin(txin.prevout);
+ }
+
}
return true;
@@ -3172,28 +3243,10 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
return DBErrors::LOAD_OK;
}
-DBErrors CWallet::ZapWalletTx(std::list<CWalletTx>& vWtx)
-{
- DBErrors nZapWalletTxRet = WalletBatch(*database,"cr+").ZapWalletTx(vWtx);
- if (nZapWalletTxRet == DBErrors::NEED_REWRITE)
- {
- if (database->Rewrite("\x04pool"))
- {
- for (const auto& spk_man_pair : m_spk_managers) {
- spk_man_pair.second->RewriteDB();
- }
- }
- }
-
- if (nZapWalletTxRet != DBErrors::LOAD_OK)
- return nZapWalletTxRet;
-
- return DBErrors::LOAD_OK;
-}
-
bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
{
bool fUpdated = false;
+ bool is_mine;
{
LOCK(cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address);
@@ -3201,8 +3254,9 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add
m_address_book[address].SetLabel(strName);
if (!strPurpose.empty()) /* update purpose only if requested */
m_address_book[address].purpose = strPurpose;
+ is_mine = IsMine(address) != ISMINE_NO;
}
- NotifyAddressBookChanged(this, address, strName, IsMine(address) != ISMINE_NO,
+ NotifyAddressBookChanged(this, address, strName, is_mine,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false;
@@ -3217,30 +3271,31 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
bool CWallet::DelAddressBook(const CTxDestination& address)
{
- // If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
- // NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
- // When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
- if (IsMine(address)) {
- WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
- return false;
- }
-
+ bool is_mine;
+ WalletBatch batch(*database);
{
LOCK(cs_wallet);
-
+ // If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
+ // NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
+ // When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
+ if (IsMine(address)) {
+ WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
+ return false;
+ }
// Delete destdata tuples associated with address
std::string strAddress = EncodeDestination(address);
for (const std::pair<const std::string, std::string> &item : m_address_book[address].destdata)
{
- WalletBatch(*database).EraseDestData(strAddress, item.first);
+ batch.EraseDestData(strAddress, item.first);
}
m_address_book.erase(address);
+ is_mine = IsMine(address) != ISMINE_NO;
}
- NotifyAddressBookChanged(this, address, "", IsMine(address) != ISMINE_NO, "", CT_DELETED);
+ NotifyAddressBookChanged(this, address, "", is_mine, "", CT_DELETED);
- WalletBatch(*database).ErasePurpose(EncodeDestination(address));
- return WalletBatch(*database).EraseName(EncodeDestination(address));
+ batch.ErasePurpose(EncodeDestination(address));
+ return batch.EraseName(EncodeDestination(address));
}
size_t CWallet::KeypoolCountExternalKeys() const
@@ -3345,7 +3400,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
{
const CWalletTx& wtx = walletEntry.second;
- if (!wtx.IsTrusted(trusted_parents))
+ if (!IsTrusted(wtx, trusted_parents))
continue;
if (wtx.IsImmatureCoinBase())
@@ -3364,9 +3419,6 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
continue;
CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
-
- if (!balances.count(addr))
- balances[addr] = 0;
balances[addr] += n;
}
}
@@ -3727,7 +3779,7 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
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:
//
@@ -3735,54 +3787,25 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
// 2. Path to an existing directory.
// 3. Path to a symlink to a directory.
// 4. For backwards compatibility, the name of a data file in -walletdir.
- LOCK(cs_wallets);
- const fs::path& wallet_path = location.GetPath();
+ const fs::path& wallet_path = fs::absolute(name, GetWalletDir());
fs::file_type path_type = fs::symlink_status(wallet_path).type();
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::regular_file && fs::path(location.GetName()).filename() == location.GetName()))) {
+ (path_type == fs::regular_file && fs::path(name).filename() == name))) {
error_string = Untranslated(strprintf(
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
"database/log.?????????? files can be stored, a location where such a directory could be created, "
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
- location.GetName(), GetWalletDir()));
- return false;
- }
-
- // 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;
+ name, GetWalletDir()));
+ status = DatabaseStatus::FAILED_BAD_PATH;
+ return nullptr;
}
+ 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();
-
- // needed to restore wallet transaction meta data after -zapwallettxes
- std::list<CWalletTx> vWtx;
-
- if (gArgs.GetBoolArg("-zapwallettxes", false)) {
- chain.initMessage(_("Zapping all transactions from wallet...").translated);
-
- std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, CreateWalletDatabase(location.GetPath()));
- DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
- if (nZapWalletRet != DBErrors::LOAD_OK) {
- error = strprintf(_("Error loading %s: Wallet corrupted"), walletFile);
- return nullptr;
- }
- }
+ const std::string& walletFile = database->Filename();
chain.initMessage(_("Loading wallet...").translated);
@@ -3790,7 +3813,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
bool fFirstRun = true;
// TODO: Can't use std::make_shared because we need a custom deleter but
// 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);
if (nLoadWalletRet != DBErrors::LOAD_OK) {
if (nLoadWalletRet == DBErrors::CORRUPT) {
@@ -4060,30 +4083,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
walletInstance->chainStateFlushed(chain.getTipLocator());
walletInstance->database->IncrementUpdateCounter();
-
- // Restore wallet transaction metadata after -zapwallettxes=1
- if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2")
- {
- WalletBatch batch(*walletInstance->database);
-
- for (const CWalletTx& wtxOld : vWtx)
- {
- uint256 hash = wtxOld.GetHash();
- std::map<uint256, CWalletTx>::iterator mi = walletInstance->mapWallet.find(hash);
- if (mi != walletInstance->mapWallet.end())
- {
- const CWalletTx* copyFrom = &wtxOld;
- CWalletTx* copyTo = &mi->second;
- copyTo->mapValue = copyFrom->mapValue;
- copyTo->vOrderForm = copyFrom->vOrderForm;
- copyTo->nTimeReceived = copyFrom->nTimeReceived;
- copyTo->nTimeSmart = copyFrom->nTimeSmart;
- copyTo->fFromMe = copyFrom->fFromMe;
- copyTo->nOrderPos = copyFrom->nOrderPos;
- batch.WriteTx(*copyTo);
- }
- }
- }
}
{