Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
// Copyright (c) 2009-2010 Satoshi Nakamoto | // Copyright (c) 2009-2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2019 The Bitcoin Core developers | // Copyright (c) 2009-2019 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <wallet/wallet.h> | #include <wallet/wallet.h> | ||||
#include <chain.h> | #include <chain.h> | ||||
#include <chainparams.h> | #include <chainparams.h> | ||||
#include <config.h> | #include <config.h> | ||||
#include <consensus/consensus.h> | #include <consensus/consensus.h> | ||||
#include <consensus/validation.h> | #include <consensus/validation.h> | ||||
#include <fs.h> | #include <fs.h> | ||||
#include <interfaces/wallet.h> | #include <interfaces/wallet.h> | ||||
#include <key.h> | #include <key.h> | ||||
#include <key_io.h> | #include <key_io.h> | ||||
#include <optional.h> | |||||
#include <policy/mempool.h> | #include <policy/mempool.h> | ||||
#include <policy/policy.h> | #include <policy/policy.h> | ||||
#include <primitives/transaction.h> | #include <primitives/transaction.h> | ||||
#include <random.h> | #include <random.h> | ||||
#include <script/descriptor.h> | #include <script/descriptor.h> | ||||
#include <script/script.h> | #include <script/script.h> | ||||
#include <script/sighashtype.h> | #include <script/sighashtype.h> | ||||
#include <script/sign.h> | #include <script/sign.h> | ||||
▲ Show 20 Lines • Show All 223 Lines • ▼ Show 20 Lines | if (!passphrase.empty() && | ||||
error = Untranslated( | error = Untranslated( | ||||
"Error: Wallet was encrypted but could not be unlocked"); | "Error: Wallet was encrypted but could not be unlocked"); | ||||
return WalletCreationStatus::ENCRYPTION_FAILED; | return WalletCreationStatus::ENCRYPTION_FAILED; | ||||
} | } | ||||
// Set a seed for the wallet | // Set a seed for the wallet | ||||
{ | { | ||||
LOCK(wallet->cs_wallet); | LOCK(wallet->cs_wallet); | ||||
if (auto spk_man = wallet->m_spk_man.get()) { | 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; | return WalletCreationStatus::CREATION_FAILED; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Relock the wallet | // Relock the wallet | ||||
▲ Show 20 Lines • Show All 381 Lines • ▼ Show 20 Lines | if (!crypter.Encrypt(_vMasterKey, kMasterKey.vchCryptedKey)) { | ||||
WalletBatch *encrypted_batch = new WalletBatch(*database); | WalletBatch *encrypted_batch = new WalletBatch(*database); | ||||
if (!encrypted_batch->TxnBegin()) { | if (!encrypted_batch->TxnBegin()) { | ||||
delete encrypted_batch; | delete encrypted_batch; | ||||
encrypted_batch = nullptr; | encrypted_batch = nullptr; | ||||
return false; | return false; | ||||
} | } | ||||
encrypted_batch->WriteMasterKey(nMasterKeyMaxID, kMasterKey); | encrypted_batch->WriteMasterKey(nMasterKeyMaxID, kMasterKey); | ||||
if (auto spk_man = m_spk_man.get()) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
auto spk_man = spk_man_pair.second.get(); | |||||
if (!spk_man->Encrypt(_vMasterKey, encrypted_batch)) { | if (!spk_man->Encrypt(_vMasterKey, encrypted_batch)) { | ||||
encrypted_batch->TxnAbort(); | encrypted_batch->TxnAbort(); | ||||
delete encrypted_batch; | delete encrypted_batch; | ||||
encrypted_batch = nullptr; | encrypted_batch = nullptr; | ||||
// We now probably have half of our keys encrypted in memory, | // We now probably have half of our keys encrypted in memory, | ||||
// and half not... die and let the user reload the unencrypted | // and half not... die and let the user reload the unencrypted | ||||
// wallet. | // wallet. | ||||
assert(false); | assert(false); | ||||
Show All 14 Lines | if (!crypter.Encrypt(_vMasterKey, kMasterKey.vchCryptedKey)) { | ||||
delete encrypted_batch; | delete encrypted_batch; | ||||
encrypted_batch = nullptr; | encrypted_batch = nullptr; | ||||
Lock(); | Lock(); | ||||
Unlock(strWalletPassphrase); | Unlock(strWalletPassphrase); | ||||
// if we are using HD, replace the HD seed with a new one | // if we are using HD, replace the HD seed with a new one | ||||
if (auto spk_man = m_spk_man.get()) { | if (auto spk_man = GetLegacyScriptPubKeyMan()) { | ||||
if (spk_man->IsHDEnabled()) { | if (spk_man->IsHDEnabled()) { | ||||
if (!spk_man->SetupGeneration(true)) { | if (!spk_man->SetupGeneration(true)) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Lock(); | Lock(); | ||||
▲ Show 20 Lines • Show All 299 Lines • ▼ Show 20 Lines | if (fExisted || IsMine(tx) || IsFromMe(tx)) { | ||||
* unused have appeared in a new transaction. If so, remove those keys | * unused have appeared in a new transaction. If so, remove those keys | ||||
* from the keypool. This can happen when restoring an old wallet backup | * from the keypool. This can happen when restoring an old wallet backup | ||||
* that does not contain the mostly recently created transactions from | * that does not contain the mostly recently created transactions from | ||||
* newer versions of the wallet. | * newer versions of the wallet. | ||||
*/ | */ | ||||
// loop though all outputs | // loop though all outputs | ||||
for (const CTxOut &txout : tx.vout) { | for (const CTxOut &txout : tx.vout) { | ||||
if (auto spk_man = m_spk_man.get()) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
spk_man->MarkUnusedAddresses(txout.scriptPubKey); | spk_man_pair.second->MarkUnusedAddresses(txout.scriptPubKey); | ||||
} | } | ||||
} | } | ||||
CWalletTx wtx(this, ptx); | CWalletTx wtx(this, ptx); | ||||
// Block disconnection override an abandoned tx as unconfirmed | // Block disconnection override an abandoned tx as unconfirmed | ||||
// which means user may have to call abandontransaction again | // which means user may have to call abandontransaction again | ||||
wtx.m_confirm = confirm; | wtx.m_confirm = confirm; | ||||
▲ Show 20 Lines • Show All 266 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
isminetype CWallet::IsMine(const CTxDestination &dest) const { | isminetype CWallet::IsMine(const CTxDestination &dest) const { | ||||
return IsMine(GetScriptForDestination(dest)); | return IsMine(GetScriptForDestination(dest)); | ||||
} | } | ||||
isminetype CWallet::IsMine(const CScript &script) const { | isminetype CWallet::IsMine(const CScript &script) const { | ||||
isminetype result = ISMINE_NO; | isminetype result = ISMINE_NO; | ||||
if (auto spk_man = m_spk_man.get()) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
result = spk_man->IsMine(script); | result = std::max(result, spk_man_pair.second->IsMine(script)); | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
Amount CWallet::GetCredit(const CTxOut &txout, | Amount CWallet::GetCredit(const CTxOut &txout, | ||||
const isminefilter &filter) const { | const isminefilter &filter) const { | ||||
if (!MoneyRange(txout.nValue)) { | if (!MoneyRange(txout.nValue)) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
▲ Show 20 Lines • Show All 118 Lines • ▼ Show 20 Lines | for (const CTxOut &txout : tx.vout) { | ||||
} | } | ||||
} | } | ||||
return nChange; | return nChange; | ||||
} | } | ||||
bool CWallet::IsHDEnabled() const { | bool CWallet::IsHDEnabled() const { | ||||
bool result = true; | bool result = true; | ||||
if (auto spk_man = m_spk_man.get()) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
result &= spk_man->IsHDEnabled(); | result &= spk_man_pair.second->IsHDEnabled(); | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
bool CWallet::CanGetAddresses(bool internal) { | bool CWallet::CanGetAddresses(bool internal) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
{ | if (m_spk_managers.empty()) { | ||||
auto spk_man = m_spk_man.get(); | return false; | ||||
} | |||||
for (OutputType t : OUTPUT_TYPES) { | |||||
auto spk_man = GetScriptPubKeyMan(t, internal); | |||||
if (spk_man && spk_man->CanGetAddresses(internal)) { | if (spk_man && spk_man->CanGetAddresses(internal)) { | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
void CWallet::SetWalletFlag(uint64_t flags) { | void CWallet::SetWalletFlag(uint64_t flags) { | ||||
▲ Show 20 Lines • Show All 1,912 Lines • ▼ Show 20 Lines | DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | ||||
// status may be not reliable. | // status may be not reliable. | ||||
auto locked_chain = LockChain(); | auto locked_chain = LockChain(); | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
fFirstRunRet = false; | fFirstRunRet = false; | ||||
DBErrors nLoadWalletRet = WalletBatch(*database, "cr+").LoadWallet(this); | DBErrors nLoadWalletRet = WalletBatch(*database, "cr+").LoadWallet(this); | ||||
if (nLoadWalletRet == DBErrors::NEED_REWRITE) { | if (nLoadWalletRet == DBErrors::NEED_REWRITE) { | ||||
if (database->Rewrite("\x04pool")) { | if (database->Rewrite("\x04pool")) { | ||||
if (auto spk_man = m_spk_man.get()) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
spk_man->RewriteDB(); | spk_man_pair.second->RewriteDB(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// This wallet is in its first run if there are no ScriptPubKeyMans and it | // This wallet is in its first run if there are no ScriptPubKeyMans and it | ||||
// isn't blank or no privkeys | // isn't blank or no privkeys | ||||
{ | fFirstRunRet = m_spk_managers.empty() && | ||||
fFirstRunRet = !m_spk_man && | |||||
!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && | !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && | ||||
!IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET); | !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET); | ||||
if (fFirstRunRet) { | |||||
assert(m_external_spk_managers.empty()); | |||||
assert(m_internal_spk_managers.empty()); | |||||
} | } | ||||
if (nLoadWalletRet != DBErrors::LOAD_OK) { | if (nLoadWalletRet != DBErrors::LOAD_OK) { | ||||
return nLoadWalletRet; | return nLoadWalletRet; | ||||
} | } | ||||
return DBErrors::LOAD_OK; | return DBErrors::LOAD_OK; | ||||
} | } | ||||
DBErrors CWallet::ZapSelectTx(std::vector<TxId> &txIdsIn, | DBErrors CWallet::ZapSelectTx(std::vector<TxId> &txIdsIn, | ||||
std::vector<TxId> &txIdsOut) { | std::vector<TxId> &txIdsOut) { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
DBErrors nZapSelectTxRet = | DBErrors nZapSelectTxRet = | ||||
WalletBatch(*database, "cr+").ZapSelectTx(txIdsIn, txIdsOut); | WalletBatch(*database, "cr+").ZapSelectTx(txIdsIn, txIdsOut); | ||||
for (const TxId &txid : txIdsOut) { | for (const TxId &txid : txIdsOut) { | ||||
const auto &it = mapWallet.find(txid); | const auto &it = mapWallet.find(txid); | ||||
wtxOrdered.erase(it->second.m_it_wtxOrdered); | wtxOrdered.erase(it->second.m_it_wtxOrdered); | ||||
mapWallet.erase(it); | mapWallet.erase(it); | ||||
NotifyTransactionChanged(this, txid, CT_DELETED); | NotifyTransactionChanged(this, txid, CT_DELETED); | ||||
} | } | ||||
if (nZapSelectTxRet == DBErrors::NEED_REWRITE) { | if (nZapSelectTxRet == DBErrors::NEED_REWRITE) { | ||||
if (database->Rewrite("\x04pool")) { | if (database->Rewrite("\x04pool")) { | ||||
if (auto spk_man = m_spk_man.get()) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
spk_man->RewriteDB(); | spk_man_pair.second->RewriteDB(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (nZapSelectTxRet != DBErrors::LOAD_OK) { | if (nZapSelectTxRet != DBErrors::LOAD_OK) { | ||||
return nZapSelectTxRet; | return nZapSelectTxRet; | ||||
} | } | ||||
MarkDirty(); | MarkDirty(); | ||||
return DBErrors::LOAD_OK; | return DBErrors::LOAD_OK; | ||||
} | } | ||||
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx> &vWtx) { | DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx> &vWtx) { | ||||
DBErrors nZapWalletTxRet = WalletBatch(*database, "cr+").ZapWalletTx(vWtx); | DBErrors nZapWalletTxRet = WalletBatch(*database, "cr+").ZapWalletTx(vWtx); | ||||
if (nZapWalletTxRet == DBErrors::NEED_REWRITE) { | if (nZapWalletTxRet == DBErrors::NEED_REWRITE) { | ||||
if (database->Rewrite("\x04pool")) { | if (database->Rewrite("\x04pool")) { | ||||
if (auto spk_man = m_spk_man.get()) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
spk_man->RewriteDB(); | spk_man_pair.second->RewriteDB(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (nZapWalletTxRet != DBErrors::LOAD_OK) { | if (nZapWalletTxRet != DBErrors::LOAD_OK) { | ||||
return nZapWalletTxRet; | return nZapWalletTxRet; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | bool CWallet::DelAddressBook(const CTxDestination &address) { | ||||
WalletBatch(*database).ErasePurpose(address); | WalletBatch(*database).ErasePurpose(address); | ||||
return WalletBatch(*database).EraseName(address); | return WalletBatch(*database).EraseName(address); | ||||
} | } | ||||
size_t CWallet::KeypoolCountExternalKeys() { | size_t CWallet::KeypoolCountExternalKeys() { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
unsigned int count = 0; | unsigned int count = 0; | ||||
if (auto spk_man = m_spk_man.get()) { | for (auto spk_man : GetActiveScriptPubKeyMans()) { | ||||
count += spk_man->KeypoolCountExternalKeys(); | count += spk_man->KeypoolCountExternalKeys(); | ||||
} | } | ||||
return count; | return count; | ||||
} | } | ||||
unsigned int CWallet::GetKeyPoolSize() const { | unsigned int CWallet::GetKeyPoolSize() const { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
unsigned int count = 0; | unsigned int count = 0; | ||||
if (auto spk_man = m_spk_man.get()) { | for (auto spk_man : GetActiveScriptPubKeyMans()) { | ||||
count += spk_man->GetKeyPoolSize(); | count += spk_man->GetKeyPoolSize(); | ||||
} | } | ||||
return count; | return count; | ||||
} | } | ||||
bool CWallet::TopUpKeyPool(unsigned int kpSize) { | bool CWallet::TopUpKeyPool(unsigned int kpSize) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
bool res = true; | bool res = true; | ||||
if (auto spk_man = m_spk_man.get()) { | for (auto spk_man : GetActiveScriptPubKeyMans()) { | ||||
res &= spk_man->TopUp(kpSize); | res &= spk_man->TopUp(kpSize); | ||||
} | } | ||||
return res; | return res; | ||||
} | } | ||||
bool CWallet::GetNewDestination(const OutputType type, const std::string label, | bool CWallet::GetNewDestination(const OutputType type, const std::string label, | ||||
CTxDestination &dest, std::string &error) { | CTxDestination &dest, std::string &error) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
error.clear(); | error.clear(); | ||||
bool result = false; | bool result = false; | ||||
auto spk_man = m_spk_man.get(); | auto spk_man = GetScriptPubKeyMan(type, false /* internal */); | ||||
if (spk_man) { | if (spk_man) { | ||||
spk_man->TopUp(); | spk_man->TopUp(); | ||||
result = spk_man->GetNewDestination(type, dest, error); | result = spk_man->GetNewDestination(type, dest, error); | ||||
} | } | ||||
if (result) { | if (result) { | ||||
SetAddressBook(dest, label, "receive"); | SetAddressBook(dest, label, "receive"); | ||||
} | } | ||||
Show All 14 Lines | bool CWallet::GetNewChangeDestination(const OutputType type, | ||||
reservedest.KeepDestination(); | reservedest.KeepDestination(); | ||||
return true; | return true; | ||||
} | } | ||||
int64_t CWallet::GetOldestKeyPoolTime() { | int64_t CWallet::GetOldestKeyPoolTime() { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
int64_t oldestKey = std::numeric_limits<int64_t>::max(); | int64_t oldestKey = std::numeric_limits<int64_t>::max(); | ||||
if (auto spk_man = m_spk_man.get()) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
oldestKey = spk_man->GetOldestKeyPoolTime(); | oldestKey = | ||||
std::min(oldestKey, spk_man_pair.second->GetOldestKeyPoolTime()); | |||||
} | } | ||||
return oldestKey; | return oldestKey; | ||||
} | } | ||||
void CWallet::MarkDestinationsDirty( | void CWallet::MarkDestinationsDirty( | ||||
const std::set<CTxDestination> &destinations) { | const std::set<CTxDestination> &destinations) { | ||||
for (auto &entry : mapWallet) { | for (auto &entry : mapWallet) { | ||||
CWalletTx &wtx = entry.second; | CWalletTx &wtx = entry.second; | ||||
▲ Show 20 Lines • Show All 179 Lines • ▼ Show 20 Lines | for (const std::pair<const CTxDestination, CAddressBookData> &item : | ||||
} | } | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
bool ReserveDestination::GetReservedDestination(CTxDestination &dest, | bool ReserveDestination::GetReservedDestination(CTxDestination &dest, | ||||
bool internal) { | bool internal) { | ||||
m_spk_man = pwallet->GetLegacyScriptPubKeyMan(); | m_spk_man = pwallet->GetScriptPubKeyMan(type, internal); | ||||
if (!m_spk_man) { | if (!m_spk_man) { | ||||
return false; | return false; | ||||
} | } | ||||
if (nIndex == -1) { | if (nIndex == -1) { | ||||
m_spk_man->TopUp(); | m_spk_man->TopUp(); | ||||
CKeyPool keypool; | CKeyPool keypool; | ||||
▲ Show 20 Lines • Show All 408 Lines • ▼ Show 20 Lines | if (gArgs.GetBoolArg("-upgradewallet", false)) { | ||||
max_version < FEATURE_PRE_SPLIT_KEYPOOL) { | max_version < FEATURE_PRE_SPLIT_KEYPOOL) { | ||||
error = | error = | ||||
_("Cannot upgrade a non HD split wallet without upgrading to " | _("Cannot upgrade a non HD split wallet without upgrading to " | ||||
"support pre split keypool. Please use -upgradewallet=200300 " | "support pre split keypool. Please use -upgradewallet=200300 " | ||||
"or -upgradewallet with no version specified."); | "or -upgradewallet with no version specified."); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (auto spk_man = walletInstance->m_spk_man.get()) { | for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) { | ||||
if (!spk_man->Upgrade(prev_version, error)) { | if (!spk_man->Upgrade(prev_version, error)) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (fFirstRun) { | if (fFirstRun) { | ||||
// Ensure this wallet.dat can only be opened by clients supporting | // Ensure this wallet.dat can only be opened by clients supporting | ||||
// HD with chain split and expects no default key. | // HD with chain split and expects no default key. | ||||
walletInstance->SetMinVersion(FEATURE_LATEST); | walletInstance->SetMinVersion(FEATURE_LATEST); | ||||
walletInstance->SetWalletFlags(wallet_creation_flags, false); | walletInstance->SetWalletFlags(wallet_creation_flags, false); | ||||
// Always create LegacyScriptPubKeyMan for now | // Always create LegacyScriptPubKeyMan for now | ||||
walletInstance->SetupLegacyScriptPubKeyMan(); | walletInstance->SetupLegacyScriptPubKeyMan(); | ||||
if (!(wallet_creation_flags & | if (!(wallet_creation_flags & | ||||
(WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) { | (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) { | ||||
LOCK(walletInstance->cs_wallet); | LOCK(walletInstance->cs_wallet); | ||||
if (auto spk_man = walletInstance->m_spk_man.get()) { | for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) { | ||||
if (!spk_man->SetupGeneration()) { | if (!spk_man->SetupGeneration()) { | ||||
error = _("Unable to generate initial keys"); | error = _("Unable to generate initial keys"); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
auto locked_chain = chain.lock(); | auto locked_chain = chain.lock(); | ||||
walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); | walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); | ||||
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { | } else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { | ||||
// Make it impossible to disable private keys after creation | // Make it impossible to disable private keys after creation | ||||
error = strprintf(_("Error loading %s: Private keys can only be " | error = strprintf(_("Error loading %s: Private keys can only be " | ||||
"disabled during creation"), | "disabled during creation"), | ||||
walletFile); | walletFile); | ||||
return nullptr; | return nullptr; | ||||
} else if (walletInstance->IsWalletFlagSet( | } else if (walletInstance->IsWalletFlagSet( | ||||
WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | ||||
if (walletInstance->m_spk_man) { | for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) { | ||||
if (walletInstance->m_spk_man->HavePrivateKeys()) { | if (spk_man->HavePrivateKeys()) { | ||||
warnings.push_back( | warnings.push_back( | ||||
strprintf(_("Warning: Private keys detected in wallet {%s} " | strprintf(_("Warning: Private keys detected in wallet {%s} " | ||||
"with disabled private keys"), | "with disabled private keys"), | ||||
walletFile)); | walletFile)); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 160 Lines • ▼ Show 20 Lines | if (tip_height && *tip_height != rescan_height) { | ||||
chain.initMessage(_("Rescanning...").translated); | chain.initMessage(_("Rescanning...").translated); | ||||
walletInstance->WalletLogPrintf( | walletInstance->WalletLogPrintf( | ||||
"Rescanning last %i blocks (from block %i)...\n", | "Rescanning last %i blocks (from block %i)...\n", | ||||
*tip_height - rescan_height, rescan_height); | *tip_height - rescan_height, rescan_height); | ||||
// No need to read and scan block if block was created before our wallet | // No need to read and scan block if block was created before our wallet | ||||
// birthday (as adjusted for block time variability) | // birthday (as adjusted for block time variability) | ||||
Optional<int64_t> time_first_key; | // The way the 'time_first_key' is initialized is just a workaround for | ||||
if (auto spk_man = walletInstance->m_spk_man.get()) { | // the gcc bug #47679 since version 4.6.0. | ||||
Optional<int64_t> time_first_key = MakeOptional(false, int64_t()); | |||||
for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) { | |||||
int64_t time = spk_man->GetTimeFirstKey(); | int64_t time = spk_man->GetTimeFirstKey(); | ||||
if (!time_first_key || time < *time_first_key) { | if (!time_first_key || time < *time_first_key) { | ||||
time_first_key = time; | time_first_key = time; | ||||
} | } | ||||
} | } | ||||
if (time_first_key) { | if (time_first_key) { | ||||
if (Optional<int> first_block = | if (Optional<int> first_block = | ||||
locked_chain->findFirstBlockWithTimeAndHeight( | locked_chain->findFirstBlockWithTimeAndHeight( | ||||
▲ Show 20 Lines • Show All 222 Lines • ▼ Show 20 Lines | bool CWallet::Lock() { | ||||
NotifyStatusChanged(this); | NotifyStatusChanged(this); | ||||
return true; | return true; | ||||
} | } | ||||
bool CWallet::Unlock(const CKeyingMaterial &vMasterKeyIn, bool accept_no_keys) { | bool CWallet::Unlock(const CKeyingMaterial &vMasterKeyIn, bool accept_no_keys) { | ||||
{ | { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (m_spk_man) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
if (!m_spk_man->CheckDecryptionKey(vMasterKeyIn, accept_no_keys)) { | if (!spk_man_pair.second->CheckDecryptionKey(vMasterKeyIn, | ||||
accept_no_keys)) { | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
vMasterKey = vMasterKeyIn; | vMasterKey = vMasterKeyIn; | ||||
} | } | ||||
NotifyStatusChanged(this); | NotifyStatusChanged(this); | ||||
return true; | return true; | ||||
} | } | ||||
std::set<ScriptPubKeyMan *> CWallet::GetActiveScriptPubKeyMans() const { | |||||
std::set<ScriptPubKeyMan *> spk_mans; | |||||
for (bool internal : {false, true}) { | |||||
for (OutputType t : OUTPUT_TYPES) { | |||||
auto spk_man = GetScriptPubKeyMan(t, internal); | |||||
if (spk_man) { | |||||
spk_mans.insert(spk_man); | |||||
} | |||||
} | |||||
} | |||||
return spk_mans; | |||||
} | |||||
std::set<ScriptPubKeyMan *> CWallet::GetAllScriptPubKeyMans() const { | |||||
std::set<ScriptPubKeyMan *> spk_mans; | |||||
for (const auto &spk_man_pair : m_spk_managers) { | |||||
spk_mans.insert(spk_man_pair.second.get()); | |||||
} | |||||
return spk_mans; | |||||
} | |||||
ScriptPubKeyMan *CWallet::GetScriptPubKeyMan(const OutputType &type, | |||||
bool internal) const { | |||||
const std::map<OutputType, ScriptPubKeyMan *> &spk_managers = | |||||
internal ? m_internal_spk_managers : m_external_spk_managers; | |||||
std::map<OutputType, ScriptPubKeyMan *>::const_iterator it = | |||||
spk_managers.find(type); | |||||
if (it == spk_managers.end()) { | |||||
WalletLogPrintf( | |||||
"%s scriptPubKey Manager for output type %d does not exist\n", | |||||
internal ? "Internal" : "External", static_cast<int>(type)); | |||||
return nullptr; | |||||
} | |||||
return it->second; | |||||
} | |||||
ScriptPubKeyMan *CWallet::GetScriptPubKeyMan(const CScript &script) const { | ScriptPubKeyMan *CWallet::GetScriptPubKeyMan(const CScript &script) const { | ||||
return m_spk_man.get(); | SignatureData sigdata; | ||||
for (const auto &spk_man_pair : m_spk_managers) { | |||||
if (spk_man_pair.second->CanProvide(script, sigdata)) { | |||||
return spk_man_pair.second.get(); | |||||
} | |||||
} | |||||
return nullptr; | |||||
} | |||||
ScriptPubKeyMan *CWallet::GetScriptPubKeyMan(const uint256 &id) const { | |||||
if (m_spk_managers.count(id) > 0) { | |||||
return m_spk_managers.at(id).get(); | |||||
} | |||||
return nullptr; | |||||
} | } | ||||
const SigningProvider * | const SigningProvider * | ||||
CWallet::GetSigningProvider(const CScript &script) const { | CWallet::GetSigningProvider(const CScript &script) const { | ||||
return m_spk_man.get(); | SignatureData sigdata; | ||||
return GetSigningProvider(script, sigdata); | |||||
} | } | ||||
const SigningProvider * | const SigningProvider * | ||||
CWallet::GetSigningProvider(const CScript &script, | CWallet::GetSigningProvider(const CScript &script, | ||||
SignatureData &sigdata) const { | SignatureData &sigdata) const { | ||||
return m_spk_man.get(); | for (const auto &spk_man_pair : m_spk_managers) { | ||||
if (spk_man_pair.second->CanProvide(script, sigdata)) { | |||||
return spk_man_pair.second->GetSigningProvider(script); | |||||
} | |||||
} | |||||
return nullptr; | |||||
} | } | ||||
LegacyScriptPubKeyMan *CWallet::GetLegacyScriptPubKeyMan() const { | LegacyScriptPubKeyMan *CWallet::GetLegacyScriptPubKeyMan() const { | ||||
return m_spk_man.get(); | // Legacy wallets only have one ScriptPubKeyMan which is a | ||||
// LegacyScriptPubKeyMan. Everything in m_internal_spk_managers and | |||||
// m_external_spk_managers point to the same legacyScriptPubKeyMan. | |||||
auto it = m_internal_spk_managers.find(OutputType::LEGACY); | |||||
if (it == m_internal_spk_managers.end()) { | |||||
return nullptr; | |||||
} | |||||
return dynamic_cast<LegacyScriptPubKeyMan *>(it->second); | |||||
} | } | ||||
LegacyScriptPubKeyMan *CWallet::GetOrCreateLegacyScriptPubKeyMan() { | LegacyScriptPubKeyMan *CWallet::GetOrCreateLegacyScriptPubKeyMan() { | ||||
SetupLegacyScriptPubKeyMan(); | SetupLegacyScriptPubKeyMan(); | ||||
return GetLegacyScriptPubKeyMan(); | return GetLegacyScriptPubKeyMan(); | ||||
} | } | ||||
void CWallet::SetupLegacyScriptPubKeyMan() { | void CWallet::SetupLegacyScriptPubKeyMan() { | ||||
if (!m_spk_man) { | if (!m_internal_spk_managers.empty() || !m_external_spk_managers.empty() || | ||||
m_spk_man = std::make_unique<LegacyScriptPubKeyMan>(*this); | !m_spk_managers.empty()) { | ||||
return; | |||||
} | |||||
auto spk_manager = | |||||
std::unique_ptr<ScriptPubKeyMan>(new LegacyScriptPubKeyMan(*this)); | |||||
for (const auto &type : OUTPUT_TYPES) { | |||||
m_internal_spk_managers[type] = spk_manager.get(); | |||||
m_external_spk_managers[type] = spk_manager.get(); | |||||
} | } | ||||
m_spk_managers[spk_manager->GetID()] = std::move(spk_manager); | |||||
} | } | ||||
const CKeyingMaterial &CWallet::GetEncryptionKey() const { | const CKeyingMaterial &CWallet::GetEncryptionKey() const { | ||||
return vMasterKey; | return vMasterKey; | ||||
} | } | ||||
bool CWallet::HasEncryptionKeys() const { | bool CWallet::HasEncryptionKeys() const { | ||||
return !mapMasterKeys.empty(); | return !mapMasterKeys.empty(); | ||||
} | } |