Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 1,099 Lines • ▼ Show 20 Lines | |||||
void CWallet::MarkDirty() { | void CWallet::MarkDirty() { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
for (std::pair<const TxId, CWalletTx> &item : mapWallet) { | for (std::pair<const TxId, CWalletTx> &item : mapWallet) { | ||||
item.second.MarkDirty(); | item.second.MarkDirty(); | ||||
} | } | ||||
} | } | ||||
void CWallet::SetUsedDestinationState(const TxId &hash, unsigned int n, | |||||
bool used) { | |||||
const CWalletTx *srctx = GetWalletTx(hash); | |||||
if (!srctx) { | |||||
return; | |||||
} | |||||
CTxDestination dst; | |||||
if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) { | |||||
if (::IsMine(*this, dst)) { | |||||
LOCK(cs_wallet); | |||||
if (used && !GetDestData(dst, "used", nullptr)) { | |||||
// p for "present", opposite of absent (null) | |||||
AddDestData(dst, "used", "p"); | |||||
} else if (!used && GetDestData(dst, "used", nullptr)) { | |||||
EraseDestData(dst, "used"); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
bool CWallet::IsUsedDestination(const CTxDestination &dst) const { | |||||
LOCK(cs_wallet); | |||||
return ::IsMine(*this, dst) && GetDestData(dst, "used", nullptr); | |||||
} | |||||
bool CWallet::IsUsedDestination(const TxId &txid, unsigned int n) const { | |||||
CTxDestination dst; | |||||
const CWalletTx *srctx = GetWalletTx(txid); | |||||
return srctx && ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst) && | |||||
IsUsedDestination(dst); | |||||
} | |||||
bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) { | bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
WalletBatch batch(*database, "r+", fFlushOnClose); | WalletBatch batch(*database, "r+", fFlushOnClose); | ||||
const TxId &txid = wtxIn.GetId(); | const TxId &txid = wtxIn.GetId(); | ||||
if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) { | |||||
// Mark used destinations | |||||
for (const CTxIn &txin : wtxIn.tx->vin) { | |||||
const COutPoint &op = txin.prevout; | |||||
SetUsedDestinationState(op.GetTxId(), op.GetN(), true); | |||||
} | |||||
} | |||||
// Inserts only if not already there, returns tx inserted or tx found. | // Inserts only if not already there, returns tx inserted or tx found. | ||||
std::pair<std::map<TxId, CWalletTx>::iterator, bool> ret = | std::pair<std::map<TxId, CWalletTx>::iterator, bool> ret = | ||||
mapWallet.insert(std::make_pair(txid, wtxIn)); | mapWallet.insert(std::make_pair(txid, wtxIn)); | ||||
CWalletTx &wtx = (*ret.first).second; | CWalletTx &wtx = (*ret.first).second; | ||||
wtx.BindWallet(this); | wtx.BindWallet(this); | ||||
bool fInsertedNew = ret.second; | bool fInsertedNew = ret.second; | ||||
if (fInsertedNew) { | if (fInsertedNew) { | ||||
wtx.nTimeReceived = chain().getAdjustedTime(); | wtx.nTimeReceived = chain().getAdjustedTime(); | ||||
▲ Show 20 Lines • Show All 1,242 Lines • ▼ Show 20 Lines | Amount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock &locked_chain, | ||||
const isminefilter &filter) const { | const isminefilter &filter) const { | ||||
if (pwallet == nullptr) { | if (pwallet == nullptr) { | ||||
return Amount::zero(); | return Amount::zero(); | ||||
} | } | ||||
// Avoid caching ismine for NO or ALL cases (could remove this check and | // Avoid caching ismine for NO or ALL cases (could remove this check and | ||||
// simplify in the future). | // simplify in the future). | ||||
bool allow_cache = | bool allow_cache = | ||||
filter == ISMINE_SPENDABLE || filter == ISMINE_WATCH_ONLY; | (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL; | ||||
// Must wait until coinbase is safely deep enough in the chain before | // Must wait until coinbase is safely deep enough in the chain before | ||||
// valuing it. | // valuing it. | ||||
if (IsImmatureCoinBase(locked_chain)) { | if (IsImmatureCoinBase(locked_chain)) { | ||||
return Amount::zero(); | return Amount::zero(); | ||||
} | } | ||||
if (fUseCache && allow_cache && | if (fUseCache && allow_cache && | ||||
m_amounts[AVAILABLE_CREDIT].m_cached[filter]) { | m_amounts[AVAILABLE_CREDIT].m_cached[filter]) { | ||||
return m_amounts[AVAILABLE_CREDIT].m_value[filter]; | return m_amounts[AVAILABLE_CREDIT].m_value[filter]; | ||||
} | } | ||||
bool allow_used_addresses = | |||||
(filter & ISMINE_USED) || | |||||
!pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE); | |||||
Amount nCredit = Amount::zero(); | Amount nCredit = Amount::zero(); | ||||
const TxId &txid = GetId(); | const TxId &txid = GetId(); | ||||
for (uint32_t i = 0; i < tx->vout.size(); i++) { | for (uint32_t i = 0; i < tx->vout.size(); i++) { | ||||
if (!pwallet->IsSpent(locked_chain, COutPoint(txid, i))) { | if (!pwallet->IsSpent(locked_chain, COutPoint(txid, i)) && | ||||
(allow_used_addresses || !pwallet->IsUsedDestination(txid, i))) { | |||||
const CTxOut &txout = tx->vout[i]; | const CTxOut &txout = tx->vout[i]; | ||||
nCredit += pwallet->GetCredit(txout, filter); | nCredit += pwallet->GetCredit(txout, filter); | ||||
if (!MoneyRange(nCredit)) { | if (!MoneyRange(nCredit)) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
" : value out of range"); | " : value out of range"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 159 Lines • ▼ Show 20 Lines | void MaybeResendWalletTxs() { | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @defgroup Actions | * @defgroup Actions | ||||
* | * | ||||
* @{ | * @{ | ||||
*/ | */ | ||||
CWallet::Balance CWallet::GetBalance(const int min_depth) const { | CWallet::Balance CWallet::GetBalance(const int min_depth, | ||||
bool avoid_reuse) const { | |||||
Balance ret; | Balance ret; | ||||
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); | ||||
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)}; | const bool is_trusted{wtx.IsTrusted(*locked_chain)}; | ||||
const int tx_depth{wtx.GetDepthInMainChain(*locked_chain)}; | const int tx_depth{wtx.GetDepthInMainChain(*locked_chain)}; | ||||
const Amount tx_credit_mine{wtx.GetAvailableCredit( | const Amount tx_credit_mine{ | ||||
*locked_chain, /* fUseCache */ true, ISMINE_SPENDABLE)}; | wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, | ||||
const Amount tx_credit_watchonly{wtx.GetAvailableCredit( | ISMINE_SPENDABLE | reuse_filter)}; | ||||
*locked_chain, /* fUseCache */ true, ISMINE_WATCH_ONLY)}; | const Amount tx_credit_watchonly{ | ||||
wtx.GetAvailableCredit(*locked_chain, /* 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; | ||||
} | } | ||||
if (!is_trusted && tx_depth == 0 && wtx.InMempool()) { | if (!is_trusted && tx_depth == 0 && wtx.InMempool()) { | ||||
ret.m_mine_untrusted_pending += tx_credit_mine; | ret.m_mine_untrusted_pending += tx_credit_mine; | ||||
ret.m_watchonly_untrusted_pending += tx_credit_watchonly; | ret.m_watchonly_untrusted_pending += tx_credit_watchonly; | ||||
} | } | ||||
Show All 26 Lines | void CWallet::AvailableCoins(interfaces::Chain::Lock &locked_chain, | ||||
const Amount nMaximumAmount, | const Amount nMaximumAmount, | ||||
const Amount nMinimumSumAmount, | const Amount nMinimumSumAmount, | ||||
const uint64_t nMaximumCount, const int nMinDepth, | const uint64_t nMaximumCount, const int nMinDepth, | ||||
const int nMaxDepth) const { | const int nMaxDepth) 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 | |||||
// 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 | |||||
// to false, do we allow already used addresses | |||||
bool allow_used_addresses = | |||||
!IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || | |||||
(coinControl && !coinControl->m_avoid_address_reuse); | |||||
const Consensus::Params params = Params().GetConsensus(); | const Consensus::Params params = Params().GetConsensus(); | ||||
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; | ||||
CValidationState state; | CValidationState state; | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | for (const auto &entry : mapWallet) { | ||||
} | } | ||||
isminetype mine = IsMine(wtx.tx->vout[i]); | isminetype mine = IsMine(wtx.tx->vout[i]); | ||||
if (mine == ISMINE_NO) { | if (mine == ISMINE_NO) { | ||||
continue; | continue; | ||||
} | } | ||||
if (!allow_used_addresses && IsUsedDestination(wtxid, i)) { | |||||
continue; | |||||
} | |||||
bool solvable = IsSolvable(*this, wtx.tx->vout[i].scriptPubKey); | bool solvable = IsSolvable(*this, wtx.tx->vout[i].scriptPubKey); | ||||
bool spendable = | bool spendable = | ||||
((mine & ISMINE_SPENDABLE) != ISMINE_NO) || | ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || | ||||
(((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && | (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && | ||||
(coinControl && coinControl->fAllowWatchOnly && solvable)); | (coinControl && coinControl->fAllowWatchOnly && solvable)); | ||||
vCoins.push_back( | vCoins.push_back( | ||||
COutput(&wtx, i, nDepth, spendable, solvable, safeTx, | COutput(&wtx, i, nDepth, spendable, solvable, safeTx, | ||||
▲ Show 20 Lines • Show All 2,031 Lines • ▼ Show 20 Lines | if (gArgs.GetBoolArg("-upgradewallet", false)) { | ||||
} | } | ||||
} | } | ||||
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); | ||||
if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | walletInstance->SetWalletFlags(wallet_creation_flags, false); | ||||
// selective allow to set flags | if (!(wallet_creation_flags & | ||||
walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS); | (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) { | ||||
} else if (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET) { | |||||
walletInstance->SetWalletFlag(WALLET_FLAG_BLANK_WALLET); | |||||
} else { | |||||
// generate a new seed | // generate a new seed | ||||
CPubKey seed = walletInstance->GenerateNewSeed(); | CPubKey seed = walletInstance->GenerateNewSeed(); | ||||
walletInstance->SetHDSeed(seed); | walletInstance->SetHDSeed(seed); | ||||
} // Otherwise, do not generate a new seed | } | ||||
// Top up the keypool | // Top up the keypool | ||||
if (walletInstance->CanGenerateKeys() && | if (walletInstance->CanGenerateKeys() && | ||||
!walletInstance->TopUpKeyPool()) { | !walletInstance->TopUpKeyPool()) { | ||||
chain.initError(_("Unable to generate initial keys").translated); | chain.initError(_("Unable to generate initial keys").translated); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 404 Lines • Show Last 20 Lines |