Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 1,014 Lines • ▼ Show 20 Lines | if (fExisted || IsMine(tx) || IsFromMe(tx)) { | ||||
return AddToWallet(wtx, false); | return AddToWallet(wtx, false); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool CWallet::TransactionCanBeAbandoned(const TxId &txid) const { | bool CWallet::TransactionCanBeAbandoned(const TxId &txid) const { | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
const CWalletTx *wtx = GetWalletTx(txid); | const CWalletTx *wtx = GetWalletTx(txid); | ||||
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && | return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && | ||||
!wtx->InMempool(); | !wtx->InMempool(); | ||||
} | } | ||||
void CWallet::MarkInputsDirty(const CTransactionRef &tx) { | void CWallet::MarkInputsDirty(const CTransactionRef &tx) { | ||||
for (const CTxIn &txin : tx->vin) { | for (const CTxIn &txin : tx->vin) { | ||||
auto it = mapWallet.find(txin.prevout.GetTxId()); | auto it = mapWallet.find(txin.prevout.GetTxId()); | ||||
if (it != mapWallet.end()) { | if (it != mapWallet.end()) { | ||||
it->second.MarkDirty(); | it->second.MarkDirty(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool CWallet::AbandonTransaction(const TxId &txid) { | bool CWallet::AbandonTransaction(const TxId &txid) { | ||||
// Temporary. Removed in upcoming lock cleanup | |||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
WalletBatch batch(*database, "r+"); | WalletBatch batch(*database, "r+"); | ||||
std::set<TxId> todo; | std::set<TxId> todo; | ||||
std::set<TxId> done; | std::set<TxId> done; | ||||
// Can't mark abandoned if confirmed or in mempool | // Can't mark abandoned if confirmed or in mempool | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | while (!todo.empty()) { | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void CWallet::MarkConflicted(const BlockHash &hashBlock, int conflicting_height, | void CWallet::MarkConflicted(const BlockHash &hashBlock, int conflicting_height, | ||||
const TxId &txid) { | const TxId &txid) { | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
int conflictconfirms = | int conflictconfirms = | ||||
(m_last_block_processed_height - conflicting_height + 1) * -1; | (m_last_block_processed_height - conflicting_height + 1) * -1; | ||||
// If number of conflict confirms cannot be determined, this means that the | // If number of conflict confirms cannot be determined, this means that the | ||||
// block is still unknown or not yet part of the main chain, for example | // block is still unknown or not yet part of the main chain, for example | ||||
// when loading the wallet during a reindex. Do nothing in that case. | // when loading the wallet during a reindex. Do nothing in that case. | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | void CWallet::SyncTransaction(const CTransactionRef &ptx, | ||||
// If a transaction changes 'conflicted' state, that changes the balance | // If a transaction changes 'conflicted' state, that changes the balance | ||||
// available of the outputs it spends. So force those to be | // available of the outputs it spends. So force those to be | ||||
// recomputed, also: | // recomputed, also: | ||||
MarkInputsDirty(ptx); | MarkInputsDirty(ptx); | ||||
} | } | ||||
void CWallet::TransactionAddedToMempool(const CTransactionRef &ptx) { | void CWallet::TransactionAddedToMempool(const CTransactionRef &ptx) { | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, | CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, | ||||
/* block_height */ 0, BlockHash(), | /* block_height */ 0, BlockHash(), | ||||
/* nIndex */ 0); | /* nIndex */ 0); | ||||
SyncTransaction(ptx, confirm); | SyncTransaction(ptx, confirm); | ||||
auto it = mapWallet.find(ptx->GetId()); | auto it = mapWallet.find(ptx->GetId()); | ||||
if (it != mapWallet.end()) { | if (it != mapWallet.end()) { | ||||
it->second.fInMempool = true; | it->second.fInMempool = true; | ||||
} | } | ||||
} | } | ||||
void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) { | void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
auto it = mapWallet.find(ptx->GetId()); | auto it = mapWallet.find(ptx->GetId()); | ||||
if (it != mapWallet.end()) { | if (it != mapWallet.end()) { | ||||
it->second.fInMempool = false; | it->second.fInMempool = false; | ||||
} | } | ||||
} | } | ||||
void CWallet::BlockConnected(const CBlock &block, | void CWallet::BlockConnected(const CBlock &block, | ||||
const std::vector<CTransactionRef> &vtxConflicted, | const std::vector<CTransactionRef> &vtxConflicted, | ||||
int height) { | int height) { | ||||
const BlockHash &block_hash = block.GetHash(); | const BlockHash &block_hash = block.GetHash(); | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
m_last_block_processed_height = height; | m_last_block_processed_height = height; | ||||
m_last_block_processed = block_hash; | m_last_block_processed = block_hash; | ||||
for (size_t index = 0; index < block.vtx.size(); index++) { | for (size_t index = 0; index < block.vtx.size(); index++) { | ||||
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, | CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, | ||||
block_hash, index); | block_hash, index); | ||||
SyncTransaction(block.vtx[index], confirm); | SyncTransaction(block.vtx[index], confirm); | ||||
TransactionRemovedFromMempool(block.vtx[index]); | TransactionRemovedFromMempool(block.vtx[index]); | ||||
} | } | ||||
for (const CTransactionRef &ptx : vtxConflicted) { | for (const CTransactionRef &ptx : vtxConflicted) { | ||||
TransactionRemovedFromMempool(ptx); | TransactionRemovedFromMempool(ptx); | ||||
} | } | ||||
} | } | ||||
void CWallet::BlockDisconnected(const CBlock &block, int height) { | void CWallet::BlockDisconnected(const CBlock &block, int height) { | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
// At block disconnection, this will change an abandoned transaction to | // At block disconnection, this will change an abandoned transaction to | ||||
// be unconfirmed, whether or not the transaction is added back to the | // be unconfirmed, whether or not the transaction is added back to the | ||||
// mempool. User may have to call abandontransaction again. It may be | // mempool. User may have to call abandontransaction again. It may be | ||||
// addressed in the future with a stickier abandoned state or even removing | // addressed in the future with a stickier abandoned state or even removing | ||||
// abandontransaction call. | // abandontransaction call. | ||||
m_last_block_processed_height = height - 1; | m_last_block_processed_height = height - 1; | ||||
▲ Show 20 Lines • Show All 575 Lines • ▼ Show 20 Lines | while (!fAbortRescan && !chain().shutdownRequested()) { | ||||
} | } | ||||
CBlock block; | CBlock block; | ||||
bool next_block; | bool next_block; | ||||
BlockHash next_block_hash; | BlockHash next_block_hash; | ||||
bool reorg = false; | bool reorg = false; | ||||
if (chain().findBlock(block_hash, FoundBlock().data(block)) && | if (chain().findBlock(block_hash, FoundBlock().data(block)) && | ||||
!block.IsNull()) { | !block.IsNull()) { | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
next_block = chain().findNextBlock( | next_block = chain().findNextBlock( | ||||
block_hash, block_height, FoundBlock().hash(next_block_hash), | block_hash, block_height, FoundBlock().hash(next_block_hash), | ||||
&reorg); | &reorg); | ||||
if (reorg) { | if (reorg) { | ||||
// Abort scan if current block is no longer active, to prevent | // Abort scan if current block is no longer active, to prevent | ||||
// marking transactions as coming from the wrong block. | // marking transactions as coming from the wrong block. | ||||
// TODO: This should return success instead of failure, see | // TODO: This should return success instead of failure, see | ||||
Show All 21 Lines | while (!fAbortRescan && !chain().shutdownRequested()) { | ||||
next_block = chain().findNextBlock( | next_block = chain().findNextBlock( | ||||
block_hash, block_height, FoundBlock().hash(next_block_hash), | block_hash, block_height, FoundBlock().hash(next_block_hash), | ||||
&reorg); | &reorg); | ||||
} | } | ||||
if (max_height && block_height >= *max_height) { | if (max_height && block_height >= *max_height) { | ||||
break; | break; | ||||
} | } | ||||
{ | { | ||||
auto locked_chain = chain().lock(); | |||||
if (!next_block || reorg) { | if (!next_block || reorg) { | ||||
// break successfully when rescan has reached the tip, or | // break successfully when rescan has reached the tip, or | ||||
// previous block is no longer on the chain due to a reorg | // previous block is no longer on the chain due to a reorg | ||||
break; | break; | ||||
} | } | ||||
// increment block and verification progress | // increment block and verification progress | ||||
block_hash = next_block_hash; | block_hash = next_block_hash; | ||||
▲ Show 20 Lines • Show All 340 Lines • ▼ Show 20 Lines | void CWallet::ResendWalletTransactions() { | ||||
if (m_best_block_time < nLastResend) { | if (m_best_block_time < nLastResend) { | ||||
return; | return; | ||||
} | } | ||||
nLastResend = GetTime(); | nLastResend = GetTime(); | ||||
int submitted_tx_count = 0; | int submitted_tx_count = 0; | ||||
{ // locked_chain and cs_wallet scope | { // cs_wallet scope | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
// Relay transactions | // Relay transactions | ||||
for (std::pair<const TxId, CWalletTx> &item : mapWallet) { | for (std::pair<const TxId, CWalletTx> &item : mapWallet) { | ||||
CWalletTx &wtx = item.second; | CWalletTx &wtx = item.second; | ||||
// Attempt to rebroadcast all txes more than 5 minutes older than | // Attempt to rebroadcast all txes more than 5 minutes older than | ||||
// the last block. SubmitMemoryPoolAndRelay() will not rebroadcast | // the last block. SubmitMemoryPoolAndRelay() will not rebroadcast | ||||
// any confirmed or conflicting txs. | // any confirmed or conflicting txs. | ||||
if (wtx.nTimeReceived > m_best_block_time - 5 * 60) { | if (wtx.nTimeReceived > m_best_block_time - 5 * 60) { | ||||
continue; | continue; | ||||
} | } | ||||
std::string unused_err_string; | std::string unused_err_string; | ||||
if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true)) { | if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true)) { | ||||
++submitted_tx_count; | ++submitted_tx_count; | ||||
} | } | ||||
} | } | ||||
} // locked_chain and cs_wallet | } // cs_wallet | ||||
if (submitted_tx_count > 0) { | if (submitted_tx_count > 0) { | ||||
WalletLogPrintf("%s: resubmit %u unconfirmed transactions\n", __func__, | WalletLogPrintf("%s: resubmit %u unconfirmed transactions\n", __func__, | ||||
submitted_tx_count); | submitted_tx_count); | ||||
} | } | ||||
} | } | ||||
/** @} */ // end of mapWallet | /** @} */ // end of mapWallet | ||||
void MaybeResendWalletTxs() { | void MaybeResendWalletTxs() { | ||||
for (const std::shared_ptr<CWallet> &pwallet : GetWallets()) { | for (const std::shared_ptr<CWallet> &pwallet : GetWallets()) { | ||||
pwallet->ResendWalletTransactions(); | pwallet->ResendWalletTransactions(); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @defgroup Actions | * @defgroup Actions | ||||
* | * | ||||
* @{ | * @{ | ||||
*/ | */ | ||||
CWallet::Balance CWallet::GetBalance(const int min_depth, | CWallet::Balance CWallet::GetBalance(const int min_depth, | ||||
bool avoid_reuse) const { | bool avoid_reuse) const { | ||||
Balance ret; | Balance ret; | ||||
isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED; | isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED; | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::set<TxId> trusted_parents; | std::set<TxId> trusted_parents; | ||||
for (const auto &entry : mapWallet) { | for (const auto &entry : mapWallet) { | ||||
const CWalletTx &wtx = entry.second; | const CWalletTx &wtx = entry.second; | ||||
const bool is_trusted{wtx.IsTrusted(trusted_parents)}; | const bool is_trusted{wtx.IsTrusted(trusted_parents)}; | ||||
const int tx_depth{wtx.GetDepthInMainChain()}; | const int tx_depth{wtx.GetDepthInMainChain()}; | ||||
const Amount tx_credit_mine{wtx.GetAvailableCredit( | const Amount tx_credit_mine{wtx.GetAvailableCredit( | ||||
/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)}; | /* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)}; | ||||
Show All 9 Lines | for (const auto &entry : mapWallet) { | ||||
} | } | ||||
ret.m_mine_immature += wtx.GetImmatureCredit(); | ret.m_mine_immature += wtx.GetImmatureCredit(); | ||||
ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit(); | ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit(); | ||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
Amount CWallet::GetAvailableBalance(const CCoinControl *coinControl) const { | Amount CWallet::GetAvailableBalance(const CCoinControl *coinControl) const { | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
Amount balance = Amount::zero(); | Amount balance = Amount::zero(); | ||||
std::vector<COutput> vCoins; | std::vector<COutput> vCoins; | ||||
AvailableCoins(vCoins, true, coinControl); | AvailableCoins(vCoins, true, coinControl); | ||||
for (const COutput &out : vCoins) { | for (const COutput &out : vCoins) { | ||||
if (out.fSpendable) { | if (out.fSpendable) { | ||||
balance += out.tx->tx->vout[out.i].nValue; | balance += out.tx->tx->vout[out.i].nValue; | ||||
▲ Show 20 Lines • Show All 481 Lines • ▼ Show 20 Lines | bool CWallet::FundTransaction(CMutableTransaction &tx, Amount &nFeeRet, | ||||
coinControl.fAllowOtherInputs = true; | coinControl.fAllowOtherInputs = true; | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
coinControl.Select(txin.prevout); | coinControl.Select(txin.prevout); | ||||
} | } | ||||
// Acquire the locks to prevent races to the new locked unspents between the | // Acquire the locks to prevent races to the new locked unspents between the | ||||
// CreateTransaction call and LockCoin calls (when lockUnspents is true). | // CreateTransaction call and LockCoin calls (when lockUnspents is true). | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
CTransactionRef tx_new; | CTransactionRef tx_new; | ||||
if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, | if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, | ||||
coinControl, false)) { | coinControl, false)) { | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | if (vecSend.empty()) { | ||||
error = _("Transaction must have at least one recipient"); | error = _("Transaction must have at least one recipient"); | ||||
return false; | return false; | ||||
} | } | ||||
CMutableTransaction txNew; | CMutableTransaction txNew; | ||||
{ | { | ||||
std::set<CInputCoin> setCoins; | std::set<CInputCoin> setCoins; | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
txNew.nLockTime = GetLocktimeForNewTransaction( | txNew.nLockTime = GetLocktimeForNewTransaction( | ||||
chain(), GetLastBlockHash(), GetLastBlockHeight()); | chain(), GetLastBlockHash(), GetLastBlockHeight()); | ||||
std::vector<COutput> vAvailableCoins; | std::vector<COutput> vAvailableCoins; | ||||
AvailableCoins(vAvailableCoins, true, &coin_control); | AvailableCoins(vAvailableCoins, true, &coin_control); | ||||
// Parameters for coin selection, init with dummy | // Parameters for coin selection, init with dummy | ||||
CoinSelectionParams coin_selection_params; | CoinSelectionParams coin_selection_params; | ||||
▲ Show 20 Lines • Show All 361 Lines • ▼ Show 20 Lines | if (res && | ||||
} | } | ||||
} | } | ||||
return res; | return res; | ||||
} | } | ||||
void CWallet::CommitTransaction( | void CWallet::CommitTransaction( | ||||
CTransactionRef tx, mapValue_t mapValue, | CTransactionRef tx, mapValue_t mapValue, | ||||
std::vector<std::pair<std::string, std::string>> orderForm) { | std::vector<std::pair<std::string, std::string>> orderForm) { | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
CWalletTx wtxNew(this, std::move(tx)); | CWalletTx wtxNew(this, std::move(tx)); | ||||
wtxNew.mapValue = std::move(mapValue); | wtxNew.mapValue = std::move(mapValue); | ||||
wtxNew.vOrderForm = std::move(orderForm); | wtxNew.vOrderForm = std::move(orderForm); | ||||
wtxNew.fTimeReceivedIsTxTime = true; | wtxNew.fTimeReceivedIsTxTime = true; | ||||
wtxNew.fFromMe = true; | wtxNew.fFromMe = true; | ||||
Show All 26 Lines | if (!wtx.SubmitMemoryPoolAndRelay(err_string, true)) { | ||||
"immediately, %s\n", | "immediately, %s\n", | ||||
err_string); | err_string); | ||||
// TODO: if we expect the failure to be long term or permanent, instead | // TODO: if we expect the failure to be long term or permanent, instead | ||||
// delete wtx from the wallet and return failure. | // delete wtx from the wallet and return failure. | ||||
} | } | ||||
} | } | ||||
DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | ||||
// Even if we don't use this lock in this function, we want to preserve | |||||
// lock order in LoadToWallet if query of chain state is needed to know | |||||
// tx status. If lock can't be taken (e.g bitcoin-wallet), tx confirmation | |||||
// status may be not reliable. | |||||
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")) { | ||||
for (const auto &spk_man_pair : m_spk_managers) { | for (const auto &spk_man_pair : m_spk_managers) { | ||||
spk_man_pair.second->RewriteDB(); | spk_man_pair.second->RewriteDB(); | ||||
▲ Show 20 Lines • Show All 218 Lines • ▼ Show 20 Lines | for (auto &entry : mapWallet) { | ||||
destinations.count(dst)) { | destinations.count(dst)) { | ||||
wtx.MarkDirty(); | wtx.MarkDirty(); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
std::map<CTxDestination, Amount> | std::map<CTxDestination, Amount> CWallet::GetAddressBalances() { | ||||
CWallet::GetAddressBalances(interfaces::Chain::Lock &locked_chain) { | |||||
std::map<CTxDestination, Amount> balances; | std::map<CTxDestination, Amount> balances; | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::set<TxId> trusted_parents; | std::set<TxId> trusted_parents; | ||||
for (const auto &walletEntry : mapWallet) { | for (const auto &walletEntry : mapWallet) { | ||||
const CWalletTx &wtx = walletEntry.second; | const CWalletTx &wtx = walletEntry.second; | ||||
if (!wtx.IsTrusted(trusted_parents)) { | if (!wtx.IsTrusted(trusted_parents)) { | ||||
▲ Show 20 Lines • Show All 219 Lines • ▼ Show 20 Lines | void CWallet::ListLockedCoins(std::vector<COutPoint> &vOutpts) const { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
for (COutPoint outpoint : setLockedCoins) { | for (COutPoint outpoint : setLockedCoins) { | ||||
vOutpts.push_back(outpoint); | vOutpts.push_back(outpoint); | ||||
} | } | ||||
} | } | ||||
/** @} */ // end of Actions | /** @} */ // end of Actions | ||||
void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock &locked_chain, | void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const { | ||||
std::map<CKeyID, int64_t> &mapKeyBirth) const { | |||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
mapKeyBirth.clear(); | mapKeyBirth.clear(); | ||||
LegacyScriptPubKeyMan *spk_man = GetLegacyScriptPubKeyMan(); | LegacyScriptPubKeyMan *spk_man = GetLegacyScriptPubKeyMan(); | ||||
assert(spk_man != nullptr); | assert(spk_man != nullptr); | ||||
LOCK(spk_man->cs_KeyStore); | LOCK(spk_man->cs_KeyStore); | ||||
// Get birth times for keys with metadata. | // Get birth times for keys with metadata. | ||||
▲ Show 20 Lines • Show All 225 Lines • ▼ Show 20 Lines | try { | ||||
return false; | return false; | ||||
} | } | ||||
if (salvage_wallet) { | if (salvage_wallet) { | ||||
// Recover readable keypairs: | // Recover readable keypairs: | ||||
CWallet dummyWallet(chainParams, &chain, WalletLocation(), | CWallet dummyWallet(chainParams, &chain, WalletLocation(), | ||||
WalletDatabase::CreateDummy()); | WalletDatabase::CreateDummy()); | ||||
std::string backup_filename; | std::string backup_filename; | ||||
// Even if we don't use this lock in this function, we want to preserve | |||||
// lock order in LoadToWallet if query of chain state is needed to know | |||||
// tx status. If lock can't be taken, tx confirmation status may be not | |||||
// reliable. | |||||
auto locked_chain = dummyWallet.LockChain(); | |||||
if (!WalletBatch::Recover( | if (!WalletBatch::Recover( | ||||
wallet_path, static_cast<void *>(&dummyWallet), | wallet_path, static_cast<void *>(&dummyWallet), | ||||
WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { | WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return WalletBatch::VerifyDatabaseFile(wallet_path, warnings, error_string); | return WalletBatch::VerifyDatabaseFile(wallet_path, warnings, error_string); | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | if (fFirstRun) { | ||||
for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) { | 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(); | |||||
walletInstance->ChainStateFlushed(chain.getTipLocator()); | walletInstance->ChainStateFlushed(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( | ||||
▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | std::shared_ptr<CWallet> CWallet::CreateWalletFromFile( | ||||
walletInstance->m_default_change_type = DEFAULT_CHANGE_TYPE; | walletInstance->m_default_change_type = DEFAULT_CHANGE_TYPE; | ||||
walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", | walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", | ||||
GetTimeMillis() - nStart); | GetTimeMillis() - nStart); | ||||
// Try to top up keypool. No-op if the wallet is locked. | // Try to top up keypool. No-op if the wallet is locked. | ||||
walletInstance->TopUpKeyPool(); | walletInstance->TopUpKeyPool(); | ||||
auto locked_chain = chain.lock(); | |||||
LOCK(walletInstance->cs_wallet); | LOCK(walletInstance->cs_wallet); | ||||
// Register wallet with validationinterface. It's done before rescan to | |||||
// avoid missing block connections between end of rescan and validation | |||||
// subscribing. Because of wallet lock being hold, block connection | |||||
// notifications are going to be pending on the validation-side until lock | |||||
// release. It's likely to have block processing duplicata (if rescan block | |||||
// range overlaps with notification one) but we guarantee at least than | |||||
// wallet state is correct after notifications delivery. This is temporary | |||||
// until rescan and notifications delivery are unified under same interface. | |||||
walletInstance->m_chain_notifications_handler = | |||||
walletInstance->chain().handleNotifications(walletInstance); | |||||
int rescan_height = 0; | int rescan_height = 0; | ||||
if (!gArgs.GetBoolArg("-rescan", false)) { | if (!gArgs.GetBoolArg("-rescan", false)) { | ||||
WalletBatch batch(*walletInstance->database); | WalletBatch batch(*walletInstance->database); | ||||
CBlockLocator locator; | CBlockLocator locator; | ||||
if (batch.ReadBestBlock(locator)) { | if (batch.ReadBestBlock(locator)) { | ||||
if (const Optional<int> fork_height = | if (const Optional<int> fork_height = | ||||
chain.findLocatorFork(locator)) { | chain.findLocatorFork(locator)) { | ||||
rescan_height = *fork_height; | rescan_height = *fork_height; | ||||
▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | std::shared_ptr<CWallet> CWallet::CreateWalletFromFile( | ||||
{ | { | ||||
LOCK(cs_wallets); | LOCK(cs_wallets); | ||||
for (auto &load_wallet : g_load_wallet_fns) { | for (auto &load_wallet : g_load_wallet_fns) { | ||||
load_wallet(interfaces::MakeWallet(walletInstance)); | load_wallet(interfaces::MakeWallet(walletInstance)); | ||||
} | } | ||||
} | } | ||||
// Register with the validation interface. It's ok to do this after rescan | |||||
// since we're still holding locked_chain. | |||||
walletInstance->m_chain_notifications_handler = | |||||
walletInstance->chain().handleNotifications(walletInstance); | |||||
walletInstance->SetBroadcastTransactions( | walletInstance->SetBroadcastTransactions( | ||||
gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); | gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); | ||||
walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n", | walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n", | ||||
walletInstance->GetKeyPoolSize()); | walletInstance->GetKeyPoolSize()); | ||||
walletInstance->WalletLogPrintf("mapWallet.size() = %u\n", | walletInstance->WalletLogPrintf("mapWallet.size() = %u\n", | ||||
walletInstance->mapWallet.size()); | walletInstance->mapWallet.size()); | ||||
walletInstance->WalletLogPrintf("m_address_book.size() = %u\n", | walletInstance->WalletLogPrintf("m_address_book.size() = %u\n", | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | for (auto spk_man : GetActiveScriptPubKeyMans()) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void CWallet::postInitProcess() { | void CWallet::postInitProcess() { | ||||
auto locked_chain = chain().lock(); | |||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
// Add wallet transactions that aren't already in a block to mempool. | // Add wallet transactions that aren't already in a block to mempool. | ||||
// Do this here as mempool requires genesis block to be loaded. | // Do this here as mempool requires genesis block to be loaded. | ||||
ReacceptWalletTransactions(); | ReacceptWalletTransactions(); | ||||
// Update wallet transactions with current mempool transactions. | // Update wallet transactions with current mempool transactions. | ||||
chain().requestMempoolTransactions(*this); | chain().requestMempoolTransactions(*this); | ||||
▲ Show 20 Lines • Show All 264 Lines • Show Last 20 Lines |