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-2016 The Bitcoin Core developers | // Copyright (c) 2009-2016 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 <checkpoints.h> | #include <checkpoints.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/chain.h> | |||||
#include <key.h> | #include <key.h> | ||||
#include <key_io.h> | #include <key_io.h> | ||||
#include <keystore.h> | #include <keystore.h> | ||||
#include <net.h> | #include <net.h> | ||||
#include <policy/policy.h> | #include <policy/policy.h> | ||||
#include <primitives/block.h> | #include <primitives/block.h> | ||||
#include <primitives/transaction.h> | #include <primitives/transaction.h> | ||||
#include <random.h> | #include <random.h> | ||||
▲ Show 20 Lines • Show All 1,140 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 { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
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) { | ||||
LOCK2(cs_main, cs_wallet); | // Temporary. Removed in upcoming lock cleanup | ||||
auto locked_chain_recursive = chain().lock(); | |||||
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 | ||||
auto it = mapWallet.find(txid); | auto it = mapWallet.find(txid); | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | while (!todo.empty()) { | ||||
MarkInputsDirty(wtx.tx); | MarkInputsDirty(wtx.tx); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void CWallet::MarkConflicted(const BlockHash &hashBlock, const TxId &txid) { | void CWallet::MarkConflicted(const BlockHash &hashBlock, const TxId &txid) { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
int conflictconfirms = 0; | int conflictconfirms = 0; | ||||
CBlockIndex *pindex = LookupBlockIndex(hashBlock); | CBlockIndex *pindex = LookupBlockIndex(hashBlock); | ||||
if (pindex && chainActive.Contains(pindex)) { | if (pindex && chainActive.Contains(pindex)) { | ||||
conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); | conflictconfirms = -(chainActive.Height() - pindex->nHeight + 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 | ||||
▲ Show 20 Lines • Show All 54 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) { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
SyncTransaction(ptx); | SyncTransaction(ptx); | ||||
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( | void CWallet::BlockConnected( | ||||
const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, | const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, | ||||
const std::vector<CTransactionRef> &vtxConflicted) { | const std::vector<CTransactionRef> &vtxConflicted) { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
// TODO: Temporarily ensure that mempool removals are notified before | // TODO: Temporarily ensure that mempool removals are notified before | ||||
// connected transactions. This shouldn't matter, but the abandoned state of | // connected transactions. This shouldn't matter, but the abandoned state of | ||||
// transactions in our wallet is currently cleared when we receive another | // transactions in our wallet is currently cleared when we receive another | ||||
// notification and there is a race condition where notification of a | // notification and there is a race condition where notification of a | ||||
// connected conflict might cause an outside process to abandon a | // connected conflict might cause an outside process to abandon a | ||||
// transaction and then have it inadvertently cleared by the notification | // transaction and then have it inadvertently cleared by the notification | ||||
// that the conflicted transaction was evicted. | // that the conflicted transaction was evicted. | ||||
for (const CTransactionRef &ptx : vtxConflicted) { | for (const CTransactionRef &ptx : vtxConflicted) { | ||||
SyncTransaction(ptx); | SyncTransaction(ptx); | ||||
TransactionRemovedFromMempool(ptx); | TransactionRemovedFromMempool(ptx); | ||||
} | } | ||||
for (size_t i = 0; i < pblock->vtx.size(); i++) { | for (size_t i = 0; i < pblock->vtx.size(); i++) { | ||||
SyncTransaction(pblock->vtx[i], pindex, i); | SyncTransaction(pblock->vtx[i], pindex, i); | ||||
TransactionRemovedFromMempool(pblock->vtx[i]); | TransactionRemovedFromMempool(pblock->vtx[i]); | ||||
} | } | ||||
m_last_block_processed = pindex; | m_last_block_processed = pindex; | ||||
} | } | ||||
void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) { | void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
for (const CTransactionRef &ptx : pblock->vtx) { | for (const CTransactionRef &ptx : pblock->vtx) { | ||||
SyncTransaction(ptx); | SyncTransaction(ptx); | ||||
} | } | ||||
} | } | ||||
void CWallet::BlockUntilSyncedToCurrentChain() { | void CWallet::BlockUntilSyncedToCurrentChain() { | ||||
AssertLockNotHeld(cs_main); | AssertLockNotHeld(cs_main); | ||||
AssertLockNotHeld(cs_wallet); | AssertLockNotHeld(cs_wallet); | ||||
{ | { | ||||
// Skip the queue-draining stuff if we know we're caught up with | // Skip the queue-draining stuff if we know we're caught up with | ||||
// chainActive.Tip()... | // chainActive.Tip()... | ||||
// We could also take cs_wallet here, and call m_last_block_processed | // We could also take cs_wallet here, and call m_last_block_processed | ||||
// protected by cs_wallet instead of cs_main, but as long as we need | // protected by cs_wallet instead of cs_main, but as long as we need | ||||
// cs_main here anyway, it's easier to just call it cs_main-protected. | // cs_main here anyway, it's easier to just call it cs_main-protected. | ||||
LOCK(cs_main); | auto locked_chain = chain().lock(); | ||||
const CBlockIndex *initialChainTip = chainActive.Tip(); | const CBlockIndex *initialChainTip = chainActive.Tip(); | ||||
if (m_last_block_processed && | if (m_last_block_processed && | ||||
m_last_block_processed->GetAncestor(initialChainTip->nHeight) == | m_last_block_processed->GetAncestor(initialChainTip->nHeight) == | ||||
initialChainTip) { | initialChainTip) { | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
// ...otherwise put a callback in the validation interface queue and wait | // ...otherwise put a callback in the validation interface queue and wait | ||||
▲ Show 20 Lines • Show All 439 Lines • ▼ Show 20 Lines | |||||
int64_t CWallet::RescanFromTime(int64_t startTime, | int64_t CWallet::RescanFromTime(int64_t startTime, | ||||
const WalletRescanReserver &reserver, | const WalletRescanReserver &reserver, | ||||
bool update) { | bool update) { | ||||
// Find starting block. May be null if nCreateTime is greater than the | // Find starting block. May be null if nCreateTime is greater than the | ||||
// highest blockchain timestamp, in which case there is nothing that needs | // highest blockchain timestamp, in which case there is nothing that needs | ||||
// to be scanned. | // to be scanned. | ||||
CBlockIndex *startBlock = nullptr; | CBlockIndex *startBlock = nullptr; | ||||
{ | { | ||||
LOCK(cs_main); | auto locked_chain = chain().lock(); | ||||
startBlock = | startBlock = | ||||
chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); | chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); | ||||
WalletLogPrintf( | WalletLogPrintf( | ||||
"%s: Rescanning last %i blocks\n", __func__, | "%s: Rescanning last %i blocks\n", __func__, | ||||
startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); | startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); | ||||
} | } | ||||
if (startBlock) { | if (startBlock) { | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if (pindex) { | ||||
// Show rescan progress in GUI as dialog or on splashscreen, if -rescan | // Show rescan progress in GUI as dialog or on splashscreen, if -rescan | ||||
// on startup. | // on startup. | ||||
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), | ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), | ||||
0); | 0); | ||||
CBlockIndex *tip = nullptr; | CBlockIndex *tip = nullptr; | ||||
double progress_begin; | double progress_begin; | ||||
double progress_end; | double progress_end; | ||||
{ | { | ||||
LOCK(cs_main); | auto locked_chain = chain().lock(); | ||||
progress_begin = | progress_begin = | ||||
GuessVerificationProgress(chainParams.TxData(), pindex); | GuessVerificationProgress(chainParams.TxData(), pindex); | ||||
if (pindexStop == nullptr) { | if (pindexStop == nullptr) { | ||||
tip = chainActive.Tip(); | tip = chainActive.Tip(); | ||||
progress_end = | progress_end = | ||||
GuessVerificationProgress(chainParams.TxData(), tip); | GuessVerificationProgress(chainParams.TxData(), tip); | ||||
} else { | } else { | ||||
progress_end = | progress_end = | ||||
Show All 15 Lines | if (pindex) { | ||||
if (GetTime() >= nNow + 60) { | if (GetTime() >= nNow + 60) { | ||||
nNow = GetTime(); | nNow = GetTime(); | ||||
WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", | WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", | ||||
pindex->nHeight, progress_current); | pindex->nHeight, progress_current); | ||||
} | } | ||||
CBlock block; | CBlock block; | ||||
if (ReadBlockFromDisk(block, pindex, chainParams.GetConsensus())) { | if (ReadBlockFromDisk(block, pindex, chainParams.GetConsensus())) { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
if (pindex && !chainActive.Contains(pindex)) { | if (pindex && !chainActive.Contains(pindex)) { | ||||
// Abort scan if current block is no longer active, to | // Abort scan if current block is no longer active, to | ||||
// prevent marking transactions as coming from the wrong | // prevent marking transactions as coming from the wrong | ||||
// block. | // block. | ||||
ret = pindex; | ret = pindex; | ||||
break; | break; | ||||
} | } | ||||
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); | for (size_t posInBlock = 0; posInBlock < block.vtx.size(); | ||||
++posInBlock) { | ++posInBlock) { | ||||
SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, | SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, | ||||
fUpdate); | fUpdate); | ||||
} | } | ||||
} else { | } else { | ||||
ret = pindex; | ret = pindex; | ||||
} | } | ||||
if (pindex == pindexStop) { | if (pindex == pindexStop) { | ||||
break; | break; | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_main); | auto locked_chain = chain().lock(); | ||||
pindex = chainActive.Next(pindex); | pindex = chainActive.Next(pindex); | ||||
progress_current = | progress_current = | ||||
GuessVerificationProgress(chainParams.TxData(), pindex); | GuessVerificationProgress(chainParams.TxData(), pindex); | ||||
if (pindexStop == nullptr && tip != chainActive.Tip()) { | if (pindexStop == nullptr && tip != chainActive.Tip()) { | ||||
tip = chainActive.Tip(); | tip = chainActive.Tip(); | ||||
// in case the tip has changed, update progress max | // in case the tip has changed, update progress max | ||||
progress_end = | progress_end = | ||||
GuessVerificationProgress(chainParams.TxData(), tip); | GuessVerificationProgress(chainParams.TxData(), tip); | ||||
Show All 19 Lines | |||||
void CWallet::ReacceptWalletTransactions() { | void CWallet::ReacceptWalletTransactions() { | ||||
// If transactions aren't being broadcasted, don't let them into local | // If transactions aren't being broadcasted, don't let them into local | ||||
// mempool either. | // mempool either. | ||||
if (!fBroadcastTransactions) { | if (!fBroadcastTransactions) { | ||||
return; | return; | ||||
} | } | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
std::map<int64_t, CWalletTx *> mapSorted; | std::map<int64_t, CWalletTx *> mapSorted; | ||||
// Sort pending wallet transactions based on their initial wallet insertion | // Sort pending wallet transactions based on their initial wallet insertion | ||||
// order. | // order. | ||||
for (std::pair<const TxId, CWalletTx> &item : mapWallet) { | for (std::pair<const TxId, CWalletTx> &item : mapWallet) { | ||||
const TxId &wtxid = item.first; | const TxId &wtxid = item.first; | ||||
CWalletTx &wtx = item.second; | CWalletTx &wtx = item.second; | ||||
assert(wtx.GetId() == wtxid); | assert(wtx.GetId() == wtxid); | ||||
▲ Show 20 Lines • Show All 296 Lines • ▼ Show 20 Lines | void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, | ||||
// Only do it if there's been a new block since last time | // Only do it if there's been a new block since last time | ||||
if (nBestBlockTime < nLastResend) { | if (nBestBlockTime < nLastResend) { | ||||
return; | return; | ||||
} | } | ||||
nLastResend = GetTime(); | nLastResend = GetTime(); | ||||
// Temporary. Removed in upcoming lock cleanup | |||||
auto locked_chain = chain().assumeLocked(); | |||||
// Rebroadcast unconfirmed txes older than 5 minutes before the last block | // Rebroadcast unconfirmed txes older than 5 minutes before the last block | ||||
// was found: | // was found: | ||||
std::vector<uint256> relayed = | std::vector<uint256> relayed = | ||||
ResendWalletTransactionsBefore(nBestBlockTime - 5 * 60, connman); | ResendWalletTransactionsBefore(nBestBlockTime - 5 * 60, connman); | ||||
if (!relayed.empty()) { | if (!relayed.empty()) { | ||||
WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", | WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", | ||||
__func__, relayed.size()); | __func__, relayed.size()); | ||||
} | } | ||||
} | } | ||||
/** @} */ // end of mapWallet | /** @} */ // end of mapWallet | ||||
/** | /** | ||||
* @defgroup Actions | * @defgroup Actions | ||||
* | * | ||||
* @{ | * @{ | ||||
*/ | */ | ||||
Amount CWallet::GetBalance(const isminefilter &filter, | Amount CWallet::GetBalance(const isminefilter &filter, | ||||
const int min_depth) const { | const int min_depth) const { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
Amount nTotal = Amount::zero(); | Amount nTotal = Amount::zero(); | ||||
for (const auto &entry : mapWallet) { | for (const auto &entry : mapWallet) { | ||||
const CWalletTx *pcoin = &entry.second; | const CWalletTx *pcoin = &entry.second; | ||||
if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) { | if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) { | ||||
nTotal += pcoin->GetAvailableCredit(true, filter); | nTotal += pcoin->GetAvailableCredit(true, filter); | ||||
} | } | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
Amount CWallet::GetUnconfirmedBalance() const { | Amount CWallet::GetUnconfirmedBalance() const { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
Amount nTotal = Amount::zero(); | Amount nTotal = Amount::zero(); | ||||
for (const auto &entry : mapWallet) { | for (const auto &entry : mapWallet) { | ||||
const CWalletTx *pcoin = &entry.second; | const CWalletTx *pcoin = &entry.second; | ||||
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && | if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && | ||||
pcoin->InMempool()) { | pcoin->InMempool()) { | ||||
nTotal += pcoin->GetAvailableCredit(); | nTotal += pcoin->GetAvailableCredit(); | ||||
} | } | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
Amount CWallet::GetImmatureBalance() const { | Amount CWallet::GetImmatureBalance() const { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
Amount nTotal = Amount::zero(); | Amount nTotal = Amount::zero(); | ||||
for (const auto &entry : mapWallet) { | for (const auto &entry : mapWallet) { | ||||
const CWalletTx *pcoin = &entry.second; | const CWalletTx *pcoin = &entry.second; | ||||
nTotal += pcoin->GetImmatureCredit(); | nTotal += pcoin->GetImmatureCredit(); | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
Amount CWallet::GetUnconfirmedWatchOnlyBalance() const { | Amount CWallet::GetUnconfirmedWatchOnlyBalance() const { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
Amount nTotal = Amount::zero(); | Amount nTotal = Amount::zero(); | ||||
for (const auto &entry : mapWallet) { | for (const auto &entry : mapWallet) { | ||||
const CWalletTx *pcoin = &entry.second; | const CWalletTx *pcoin = &entry.second; | ||||
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && | if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && | ||||
pcoin->InMempool()) { | pcoin->InMempool()) { | ||||
nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY); | nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY); | ||||
} | } | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
Amount CWallet::GetImmatureWatchOnlyBalance() const { | Amount CWallet::GetImmatureWatchOnlyBalance() const { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
Amount nTotal = Amount::zero(); | Amount nTotal = Amount::zero(); | ||||
for (const auto &entry : mapWallet) { | for (const auto &entry : mapWallet) { | ||||
const CWalletTx *pcoin = &entry.second; | const CWalletTx *pcoin = &entry.second; | ||||
nTotal += pcoin->GetImmatureWatchOnlyCredit(); | nTotal += pcoin->GetImmatureWatchOnlyCredit(); | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
// Calculate total balance in a different way from GetBalance. The biggest | // Calculate total balance in a different way from GetBalance. The biggest | ||||
// difference is that GetBalance sums up all unspent TxOuts paying to the | // difference is that GetBalance sums up all unspent TxOuts paying to the | ||||
// wallet, while this sums up both spent and unspent TxOuts paying to the | // wallet, while this sums up both spent and unspent TxOuts paying to the | ||||
// wallet, and then subtracts the values of TxIns spending from the wallet. This | // wallet, and then subtracts the values of TxIns spending from the wallet. This | ||||
// also has fewer restrictions on which unconfirmed transactions are considered | // also has fewer restrictions on which unconfirmed transactions are considered | ||||
// trusted. | // trusted. | ||||
Amount CWallet::GetLegacyBalance(const isminefilter &filter, int minDepth, | Amount CWallet::GetLegacyBalance(const isminefilter &filter, int minDepth, | ||||
const std::string *account) const { | const std::string *account) const { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
Amount balance = Amount::zero(); | Amount balance = Amount::zero(); | ||||
for (const auto &entry : mapWallet) { | for (const auto &entry : mapWallet) { | ||||
const CWalletTx &wtx = entry.second; | const CWalletTx &wtx = entry.second; | ||||
const int depth = wtx.GetDepthInMainChain(); | const int depth = wtx.GetDepthInMainChain(); | ||||
if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase()) { | if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase()) { | ||||
continue; | continue; | ||||
} | } | ||||
Show All 21 Lines | Amount CWallet::GetLegacyBalance(const isminefilter &filter, int minDepth, | ||||
if (account) { | if (account) { | ||||
balance += WalletBatch(*database).GetAccountCreditDebit(*account); | balance += WalletBatch(*database).GetAccountCreditDebit(*account); | ||||
} | } | ||||
return balance; | return balance; | ||||
} | } | ||||
Amount CWallet::GetAvailableBalance(const CCoinControl *coinControl) const { | Amount CWallet::GetAvailableBalance(const CCoinControl *coinControl) const { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
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 444 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). | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
CReserveKey reservekey(this); | CReserveKey reservekey(this); | ||||
CTransactionRef tx_new; | CTransactionRef tx_new; | ||||
if (!CreateTransaction(vecSend, tx_new, reservekey, nFeeRet, | if (!CreateTransaction(vecSend, tx_new, reservekey, nFeeRet, | ||||
nChangePosInOut, strFailReason, coinControl, | nChangePosInOut, strFailReason, coinControl, | ||||
false)) { | false)) { | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | if (GetRandInt(10) == 0) { | ||||
txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); | txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); | ||||
} | } | ||||
assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); | assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); | ||||
assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
{ | { | ||||
std::set<CInputCoin> setCoins; | std::set<CInputCoin> setCoins; | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | |||||
std::vector<COutput> vAvailableCoins; | std::vector<COutput> vAvailableCoins; | ||||
AvailableCoins(vAvailableCoins, true, &coinControl); | AvailableCoins(vAvailableCoins, true, &coinControl); | ||||
// Parameters for coin selection, init with dummy | // Parameters for coin selection, init with dummy | ||||
CoinSelectionParams coin_selection_params; | CoinSelectionParams coin_selection_params; | ||||
// Create change script that will be used if we need change | // Create change script that will be used if we need change | ||||
// TODO: pass in scriptChange instead of reservekey so | // TODO: pass in scriptChange instead of reservekey so | ||||
▲ Show 20 Lines • Show All 356 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* Call after CreateTransaction unless you want to abort | * Call after CreateTransaction unless you want to abort | ||||
*/ | */ | ||||
bool CWallet::CommitTransaction( | bool 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, | ||||
std::string fromAccount, CReserveKey &reservekey, CConnman *connman, | std::string fromAccount, CReserveKey &reservekey, CConnman *connman, | ||||
CValidationState &state) { | CValidationState &state) { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
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.strFromAccount = std::move(fromAccount); | wtxNew.strFromAccount = std::move(fromAccount); | ||||
wtxNew.fTimeReceivedIsTxTime = true; | wtxNew.fTimeReceivedIsTxTime = true; | ||||
wtxNew.fFromMe = true; | wtxNew.fFromMe = true; | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry, | ||||
laccentries.push_back(acentry); | laccentries.push_back(acentry); | ||||
CAccountingEntry &entry = laccentries.back(); | CAccountingEntry &entry = laccentries.back(); | ||||
wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair(nullptr, &entry))); | wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair(nullptr, &entry))); | ||||
return true; | return true; | ||||
} | } | ||||
DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | ||||
LOCK2(cs_main, cs_wallet); | auto locked_chain = chain().lock(); | ||||
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")) { | ||||
setInternalKeyPool.clear(); | setInternalKeyPool.clear(); | ||||
setExternalKeyPool.clear(); | setExternalKeyPool.clear(); | ||||
m_pool_key_to_index.clear(); | m_pool_key_to_index.clear(); | ||||
▲ Show 20 Lines • Show All 1,124 Lines • ▼ Show 20 Lines | if (fFirstRun) { | ||||
// Top up the keypool | // Top up the keypool | ||||
if (walletInstance->CanGenerateKeys() && | if (walletInstance->CanGenerateKeys() && | ||||
!walletInstance->TopUpKeyPool()) { | !walletInstance->TopUpKeyPool()) { | ||||
InitError(_("Unable to generate initial keys")); | InitError(_("Unable to generate initial keys")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
// Temporary. Removed in upcoming lock cleanup | |||||
auto locked_chain = chain.assumeLocked(); | |||||
walletInstance->ChainStateFlushed(chainActive.GetLocator()); | walletInstance->ChainStateFlushed(chainActive.GetLocator()); | ||||
} 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 | ||||
InitError(strprintf(_("Error loading %s: Private keys can only be " | InitError(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 83 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(); | ||||
LOCK2(cs_main, walletInstance->cs_wallet); | auto locked_chain = chain.lock(); | ||||
LOCK(walletInstance->cs_wallet); | |||||
CBlockIndex *pindexRescan = chainActive.Genesis(); | CBlockIndex *pindexRescan = chainActive.Genesis(); | ||||
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)) { | ||||
pindexRescan = FindForkInGlobalIndex(chainActive, locator); | pindexRescan = FindForkInGlobalIndex(chainActive, locator); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 254 Lines • Show Last 20 Lines |