Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 2,081 Lines • ▼ Show 20 Lines | Amount CWalletTx::GetChange() const { | ||||
fChangeCached = true; | fChangeCached = true; | ||||
return nChangeCached; | return nChangeCached; | ||||
} | } | ||||
bool CWalletTx::InMempool() const { | bool CWalletTx::InMempool() const { | ||||
return fInMempool; | return fInMempool; | ||||
} | } | ||||
bool CWalletTx::IsTrusted(interfaces::Chain::Lock &locked_chain) const { | bool CWalletTx::IsTrusted() const { | ||||
std::set<TxId> s; | std::set<TxId> s; | ||||
return IsTrusted(locked_chain, s); | return IsTrusted(s); | ||||
} | } | ||||
bool CWalletTx::IsTrusted(interfaces::Chain::Lock &locked_chain, | bool CWalletTx::IsTrusted(std::set<TxId> &trusted_parents) const { | ||||
std::set<TxId> &trusted_parents) const { | |||||
// Quick answer in most cases | // Quick answer in most cases | ||||
TxValidationState state; | TxValidationState state; | ||||
if (!locked_chain.contextualCheckTransactionForCurrentBlock( | if (!pwallet->chain().contextualCheckTransactionForCurrentBlock(*tx, | ||||
this->pwallet->chainParams.GetConsensus(), *tx, state)) { | state)) { | ||||
return false; | return false; | ||||
} | } | ||||
int nDepth = GetDepthInMainChain(); | int nDepth = GetDepthInMainChain(); | ||||
if (nDepth >= 1) { | if (nDepth >= 1) { | ||||
return true; | return true; | ||||
} | } | ||||
Show All 25 Lines | for (const CTxIn &txin : tx->vin) { | ||||
if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) { | if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) { | ||||
return false; | return false; | ||||
} | } | ||||
// If we've already trusted this parent, continue | // If we've already trusted this parent, continue | ||||
if (trusted_parents.count(parent->GetId())) { | if (trusted_parents.count(parent->GetId())) { | ||||
continue; | continue; | ||||
} | } | ||||
// Recurse to check that the parent is also trusted | // Recurse to check that the parent is also trusted | ||||
if (!parent->IsTrusted(locked_chain, trusted_parents)) { | if (!parent->IsTrusted(trusted_parents)) { | ||||
return false; | return false; | ||||
} | } | ||||
trusted_parents.insert(parent->GetId()); | trusted_parents.insert(parent->GetId()); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | 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(); | 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(*locked_chain, 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)}; | ||||
const Amount tx_credit_watchonly{wtx.GetAvailableCredit( | const Amount tx_credit_watchonly{wtx.GetAvailableCredit( | ||||
/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)}; | /* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)}; | ||||
if (is_trusted && tx_depth >= min_depth) { | if (is_trusted && tx_depth >= min_depth) { | ||||
ret.m_mine_trusted += tx_credit_mine; | ret.m_mine_trusted += tx_credit_mine; | ||||
ret.m_watchonly_trusted += tx_credit_watchonly; | ret.m_watchonly_trusted += tx_credit_watchonly; | ||||
Show All 9 Lines | |||||
} | } | ||||
Amount CWallet::GetAvailableBalance(const CCoinControl *coinControl) const { | Amount CWallet::GetAvailableBalance(const CCoinControl *coinControl) const { | ||||
auto locked_chain = chain().lock(); | 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(*locked_chain, 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; | ||||
} | } | ||||
} | } | ||||
return balance; | return balance; | ||||
} | } | ||||
void CWallet::AvailableCoins(interfaces::Chain::Lock &locked_chain, | void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, | ||||
std::vector<COutput> &vCoins, bool fOnlySafe, | |||||
const CCoinControl *coinControl, | const CCoinControl *coinControl, | ||||
const Amount nMinimumAmount, | const Amount nMinimumAmount, | ||||
const Amount nMaximumAmount, | const Amount nMaximumAmount, | ||||
const Amount nMinimumSumAmount, | const Amount nMinimumSumAmount, | ||||
const uint64_t nMaximumCount) const { | const uint64_t nMaximumCount) const { | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
vCoins.clear(); | vCoins.clear(); | ||||
Amount nTotal = Amount::zero(); | Amount nTotal = Amount::zero(); | ||||
// Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we | // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we | ||||
// always allow), or we default to avoiding, and only in the case where a | // always allow), or we default to avoiding, and only in the case where a | ||||
// coin control object is provided, and has the avoid address reuse flag set | // coin control object is provided, and has the avoid address reuse flag set | ||||
// to false, do we allow already used addresses | // to false, do we allow already used addresses | ||||
bool allow_used_addresses = | bool allow_used_addresses = | ||||
!IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || | !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || | ||||
(coinControl && !coinControl->m_avoid_address_reuse); | (coinControl && !coinControl->m_avoid_address_reuse); | ||||
const int min_depth = {coinControl ? coinControl->m_min_depth | const int min_depth = {coinControl ? coinControl->m_min_depth | ||||
: DEFAULT_MIN_DEPTH}; | : DEFAULT_MIN_DEPTH}; | ||||
const int max_depth = {coinControl ? coinControl->m_max_depth | const int max_depth = {coinControl ? coinControl->m_max_depth | ||||
: DEFAULT_MAX_DEPTH}; | : DEFAULT_MAX_DEPTH}; | ||||
const Consensus::Params params = this->chainParams.GetConsensus(); | |||||
std::set<TxId> trusted_parents; | std::set<TxId> trusted_parents; | ||||
for (const auto &entry : mapWallet) { | for (const auto &entry : mapWallet) { | ||||
const TxId &wtxid = entry.first; | const TxId &wtxid = entry.first; | ||||
const CWalletTx &wtx = entry.second; | const CWalletTx &wtx = entry.second; | ||||
TxValidationState state; | TxValidationState state; | ||||
if (!locked_chain.contextualCheckTransactionForCurrentBlock( | if (!chain().contextualCheckTransactionForCurrentBlock(*wtx.tx, | ||||
params, *wtx.tx, state)) { | state)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (wtx.IsImmatureCoinBase()) { | if (wtx.IsImmatureCoinBase()) { | ||||
continue; | continue; | ||||
} | } | ||||
int nDepth = wtx.GetDepthInMainChain(); | int nDepth = wtx.GetDepthInMainChain(); | ||||
if (nDepth < 0) { | if (nDepth < 0) { | ||||
continue; | continue; | ||||
} | } | ||||
// We should not consider coins which aren't at least in our mempool. | // We should not consider coins which aren't at least in our mempool. | ||||
// It's possible for these to be conflicted via ancestors which we may | // It's possible for these to be conflicted via ancestors which we may | ||||
// never be able to detect. | // never be able to detect. | ||||
if (nDepth == 0 && !wtx.InMempool()) { | if (nDepth == 0 && !wtx.InMempool()) { | ||||
continue; | continue; | ||||
} | } | ||||
bool safeTx = wtx.IsTrusted(locked_chain, trusted_parents); | bool safeTx = wtx.IsTrusted(trusted_parents); | ||||
// Bitcoin-ABC: Removed check that prevents consideration of coins from | // Bitcoin-ABC: Removed check that prevents consideration of coins from | ||||
// transactions that are replacing other transactions. This check based | // transactions that are replacing other transactions. This check based | ||||
// on wtx.mapValue.count("replaces_txid") which was not being set | // on wtx.mapValue.count("replaces_txid") which was not being set | ||||
// anywhere. | // anywhere. | ||||
// Similarly, we should not consider coins from transactions that have | // Similarly, we should not consider coins from transactions that have | ||||
// been replaced. In the example above, we would want to prevent | // been replaced. In the example above, we would want to prevent | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | for (const auto &entry : mapWallet) { | ||||
// Checks the maximum number of UTXO's. | // Checks the maximum number of UTXO's. | ||||
if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { | if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
std::map<CTxDestination, std::vector<COutput>> | std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const { | ||||
CWallet::ListCoins(interfaces::Chain::Lock &locked_chain) const { | |||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
std::map<CTxDestination, std::vector<COutput>> result; | std::map<CTxDestination, std::vector<COutput>> result; | ||||
std::vector<COutput> availableCoins; | std::vector<COutput> availableCoins; | ||||
AvailableCoins(locked_chain, availableCoins); | AvailableCoins(availableCoins); | ||||
for (const auto &coin : availableCoins) { | for (const auto &coin : availableCoins) { | ||||
CTxDestination address; | CTxDestination address; | ||||
if ((coin.fSpendable || | if ((coin.fSpendable || | ||||
(IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && | (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && | ||||
coin.fSolvable)) && | coin.fSolvable)) && | ||||
ExtractDestination( | ExtractDestination( | ||||
FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, | FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, | ||||
▲ Show 20 Lines • Show All 475 Lines • ▼ Show 20 Lines | bool CWallet::CreateTransactionInternal(const std::vector<CRecipient> &vecSend, | ||||
{ | { | ||||
std::set<CInputCoin> setCoins; | std::set<CInputCoin> setCoins; | ||||
auto locked_chain = chain().lock(); | 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(*locked_chain, 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; | ||||
// 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 reservedest so | // TODO: pass in scriptChange instead of reservedest so | ||||
// change transaction isn't always pay-to-bitcoin-address | // change transaction isn't always pay-to-bitcoin-address | ||||
CScript scriptChange; | CScript scriptChange; | ||||
▲ Show 20 Lines • Show All 647 Lines • ▼ Show 20 Lines | |||||
CWallet::GetAddressBalances(interfaces::Chain::Lock &locked_chain) { | 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(locked_chain, trusted_parents)) { | if (!wtx.IsTrusted(trusted_parents)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (wtx.IsImmatureCoinBase()) { | if (wtx.IsImmatureCoinBase()) { | ||||
continue; | continue; | ||||
} | } | ||||
int nDepth = wtx.GetDepthInMainChain(); | int nDepth = wtx.GetDepthInMainChain(); | ||||
▲ Show 20 Lines • Show All 556 Lines • ▼ Show 20 Lines | if (fFirstRun) { | ||||
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(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)) { | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | std::shared_ptr<CWallet> CWallet::CreateWalletFromFile( | ||||
LOCK(walletInstance->cs_wallet); | LOCK(walletInstance->cs_wallet); | ||||
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 = | ||||
locked_chain->findLocatorFork(locator)) { | chain.findLocatorFork(locator)) { | ||||
rescan_height = *fork_height; | rescan_height = *fork_height; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
const Optional<int> tip_height = chain.getHeight(); | const Optional<int> tip_height = chain.getHeight(); | ||||
if (tip_height) { | if (tip_height) { | ||||
walletInstance->m_last_block_processed = | walletInstance->m_last_block_processed = | ||||
Show All 10 Lines | if (tip_height && *tip_height != rescan_height) { | ||||
// or if they ran -disablewallet for a longer time, then decided to | // or if they ran -disablewallet for a longer time, then decided to | ||||
// re-enable | // re-enable | ||||
if (chain.havePruned()) { | if (chain.havePruned()) { | ||||
// Exit early and print an error. | // Exit early and print an error. | ||||
// If a block is pruned after this check, we will load the wallet, | // If a block is pruned after this check, we will load the wallet, | ||||
// but fail the rescan with a generic error. | // but fail the rescan with a generic error. | ||||
int block_height = *tip_height; | int block_height = *tip_height; | ||||
while (block_height > 0 && | while (block_height > 0 && | ||||
locked_chain->haveBlockOnDisk(block_height - 1) && | chain.haveBlockOnDisk(block_height - 1) && | ||||
rescan_height != block_height) { | rescan_height != block_height) { | ||||
--block_height; | --block_height; | ||||
} | } | ||||
if (rescan_height != block_height) { | if (rescan_height != block_height) { | ||||
error = _("Prune: last wallet synchronisation goes beyond " | error = _("Prune: last wallet synchronisation goes beyond " | ||||
"pruned data. You need to -reindex (download the " | "pruned data. You need to -reindex (download the " | ||||
"whole blockchain again in case of pruned node)"); | "whole blockchain again in case of pruned node)"); | ||||
Show All 14 Lines | if (tip_height && *tip_height != rescan_height) { | ||||
for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) { | 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( | chain.findFirstBlockWithTimeAndHeight( | ||||
*time_first_key - TIMESTAMP_WINDOW, rescan_height, | *time_first_key - TIMESTAMP_WINDOW, rescan_height, | ||||
nullptr)) { | nullptr)) { | ||||
rescan_height = *first_block; | rescan_height = *first_block; | ||||
} | } | ||||
} | } | ||||
{ | { | ||||
WalletRescanReserver reserver(*walletInstance); | WalletRescanReserver reserver(*walletInstance); | ||||
if (!reserver.reserve() || | if (!reserver.reserve() || | ||||
(ScanResult::SUCCESS != | (ScanResult::SUCCESS != | ||||
walletInstance | walletInstance | ||||
->ScanForWalletTransactions( | ->ScanForWalletTransactions( | ||||
chain.getBlockHash(rescan_height), rescan_height, | chain.getBlockHash(rescan_height), rescan_height, | ||||
{} /* max height */, reserver, true /* update */) | {} /* max height */, reserver, true /* update */) | ||||
.status)) { | .status)) { | ||||
error = _("Failed to rescan the wallet during initialization"); | error = _("Failed to rescan the wallet during initialization"); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); | walletInstance->ChainStateFlushed(chain.getTipLocator()); | ||||
walletInstance->database->IncrementUpdateCounter(); | walletInstance->database->IncrementUpdateCounter(); | ||||
// Restore wallet transaction metadata after -zapwallettxes=1 | // Restore wallet transaction metadata after -zapwallettxes=1 | ||||
if (gArgs.GetBoolArg("-zapwallettxes", false) && | if (gArgs.GetBoolArg("-zapwallettxes", false) && | ||||
gArgs.GetArg("-zapwallettxes", "1") != "2") { | gArgs.GetArg("-zapwallettxes", "1") != "2") { | ||||
WalletBatch batch(*walletInstance->database); | WalletBatch batch(*walletInstance->database); | ||||
for (const CWalletTx &wtxOld : vWtx) { | for (const CWalletTx &wtxOld : vWtx) { | ||||
▲ Show 20 Lines • Show All 374 Lines • Show Last 20 Lines |