Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 2,354 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
Amount CWallet::GetBalance(const isminefilter &filter, | Amount CWallet::GetBalance(const isminefilter &filter, | ||||
const int min_depth) const { | const int min_depth) const { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | 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 &wtx = entry.second; | ||||
if (pcoin->IsTrusted(*locked_chain) && | if (wtx.IsTrusted(*locked_chain) && | ||||
pcoin->GetDepthInMainChain(*locked_chain) >= min_depth) { | wtx.GetDepthInMainChain(*locked_chain) >= min_depth) { | ||||
nTotal += pcoin->GetAvailableCredit(*locked_chain, true, filter); | nTotal += wtx.GetAvailableCredit(*locked_chain, true, filter); | ||||
} | } | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
Amount CWallet::GetUnconfirmedBalance() const { | Amount CWallet::GetUnconfirmedBalance() const { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | 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 &wtx = entry.second; | ||||
if (!pcoin->IsTrusted(*locked_chain) && | if (!wtx.IsTrusted(*locked_chain) && | ||||
pcoin->GetDepthInMainChain(*locked_chain) == 0 && | wtx.GetDepthInMainChain(*locked_chain) == 0 && wtx.InMempool()) { | ||||
pcoin->InMempool()) { | nTotal += wtx.GetAvailableCredit(*locked_chain); | ||||
nTotal += pcoin->GetAvailableCredit(*locked_chain); | |||||
} | } | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
Amount CWallet::GetImmatureBalance() const { | Amount CWallet::GetImmatureBalance() const { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | 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 &wtx = entry.second; | ||||
nTotal += pcoin->GetImmatureCredit(*locked_chain); | nTotal += wtx.GetImmatureCredit(*locked_chain); | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
Amount CWallet::GetUnconfirmedWatchOnlyBalance() const { | Amount CWallet::GetUnconfirmedWatchOnlyBalance() const { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | 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 &wtx = entry.second; | ||||
if (!pcoin->IsTrusted(*locked_chain) && | if (!wtx.IsTrusted(*locked_chain) && | ||||
pcoin->GetDepthInMainChain(*locked_chain) == 0 && | wtx.GetDepthInMainChain(*locked_chain) == 0 && wtx.InMempool()) { | ||||
pcoin->InMempool()) { | nTotal += | ||||
nTotal += pcoin->GetAvailableCredit(*locked_chain, true, | wtx.GetAvailableCredit(*locked_chain, true, ISMINE_WATCH_ONLY); | ||||
ISMINE_WATCH_ONLY); | |||||
} | } | ||||
} | } | ||||
return nTotal; | return nTotal; | ||||
} | } | ||||
Amount CWallet::GetImmatureWatchOnlyBalance() const { | Amount CWallet::GetImmatureWatchOnlyBalance() const { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | 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 &wtx = entry.second; | ||||
nTotal += pcoin->GetImmatureWatchOnlyCredit(*locked_chain); | nTotal += wtx.GetImmatureWatchOnlyCredit(*locked_chain); | ||||
} | } | ||||
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 | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | void CWallet::AvailableCoins(interfaces::Chain::Lock &locked_chain, | ||||
vCoins.clear(); | vCoins.clear(); | ||||
Amount nTotal = Amount::zero(); | Amount nTotal = Amount::zero(); | ||||
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 *pcoin = &entry.second; | const CWalletTx &wtx = entry.second; | ||||
CValidationState state; | CValidationState state; | ||||
if (!locked_chain.contextualCheckTransactionForCurrentBlock( | if (!locked_chain.contextualCheckTransactionForCurrentBlock( | ||||
params, *pcoin->tx, state)) { | params, *wtx.tx, state)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (pcoin->IsImmatureCoinBase(locked_chain)) { | if (wtx.IsImmatureCoinBase(locked_chain)) { | ||||
continue; | continue; | ||||
} | } | ||||
int nDepth = pcoin->GetDepthInMainChain(locked_chain); | int nDepth = wtx.GetDepthInMainChain(locked_chain); | ||||
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 && !pcoin->InMempool()) { | if (nDepth == 0 && !wtx.InMempool()) { | ||||
continue; | continue; | ||||
} | } | ||||
bool safeTx = pcoin->IsTrusted(locked_chain); | bool safeTx = wtx.IsTrusted(locked_chain); | ||||
// 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 pcoin->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 | ||||
// creation of a transaction A' spending an output of A, because if | // creation of a transaction A' spending an output of A, because if | ||||
// transaction B were initially confirmed, conflicting with A and A', we | // transaction B were initially confirmed, conflicting with A and A', we | ||||
// wouldn't want to the user to create a transaction D intending to | // wouldn't want to the user to create a transaction D intending to | ||||
// replace A', but potentially resulting in a scenario where A, A', and | // replace A', but potentially resulting in a scenario where A, A', and | ||||
// D could all be accepted (instead of just B and D, or just A and A' | // D could all be accepted (instead of just B and D, or just A and A' | ||||
// like the user would want). | // like the user would want). | ||||
// Bitcoin-ABC: retained this check as 'replaced_by_txid' is still set | // Bitcoin-ABC: retained this check as 'replaced_by_txid' is still set | ||||
// in the wallet code. | // in the wallet code. | ||||
if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) { | if (nDepth == 0 && wtx.mapValue.count("replaced_by_txid")) { | ||||
safeTx = false; | safeTx = false; | ||||
} | } | ||||
if (fOnlySafe && !safeTx) { | if (fOnlySafe && !safeTx) { | ||||
continue; | continue; | ||||
} | } | ||||
if (nDepth < nMinDepth || nDepth > nMaxDepth) { | if (nDepth < nMinDepth || nDepth > nMaxDepth) { | ||||
continue; | continue; | ||||
} | } | ||||
for (uint32_t i = 0; i < pcoin->tx->vout.size(); i++) { | for (uint32_t i = 0; i < wtx.tx->vout.size(); i++) { | ||||
if (pcoin->tx->vout[i].nValue < nMinimumAmount || | if (wtx.tx->vout[i].nValue < nMinimumAmount || | ||||
pcoin->tx->vout[i].nValue > nMaximumAmount) { | wtx.tx->vout[i].nValue > nMaximumAmount) { | ||||
continue; | continue; | ||||
} | } | ||||
const COutPoint outpoint(wtxid, i); | const COutPoint outpoint(wtxid, i); | ||||
if (coinControl && coinControl->HasSelected() && | if (coinControl && coinControl->HasSelected() && | ||||
!coinControl->fAllowOtherInputs && | !coinControl->fAllowOtherInputs && | ||||
!coinControl->IsSelected(outpoint)) { | !coinControl->IsSelected(outpoint)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (IsLockedCoin(outpoint)) { | if (IsLockedCoin(outpoint)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (IsSpent(locked_chain, outpoint)) { | if (IsSpent(locked_chain, outpoint)) { | ||||
continue; | continue; | ||||
} | } | ||||
isminetype mine = IsMine(pcoin->tx->vout[i]); | isminetype mine = IsMine(wtx.tx->vout[i]); | ||||
if (mine == ISMINE_NO) { | if (mine == ISMINE_NO) { | ||||
continue; | continue; | ||||
} | } | ||||
bool solvable = IsSolvable(*this, pcoin->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(pcoin, i, nDepth, spendable, solvable, safeTx, | COutput(&wtx, i, nDepth, spendable, solvable, safeTx, | ||||
(coinControl && coinControl->fAllowWatchOnly))); | (coinControl && coinControl->fAllowWatchOnly))); | ||||
// Checks the sum amount of all UTXO's. | // Checks the sum amount of all UTXO's. | ||||
if (nMinimumSumAmount != MAX_MONEY) { | if (nMinimumSumAmount != MAX_MONEY) { | ||||
nTotal += pcoin->tx->vout[i].nValue; | nTotal += wtx.tx->vout[i].nValue; | ||||
if (nTotal >= nMinimumSumAmount) { | if (nTotal >= nMinimumSumAmount) { | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
// 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) { | ||||
▲ Show 20 Lines • Show All 187 Lines • ▼ Show 20 Lines | for (const COutPoint &outpoint : vPresetInputs) { | ||||
std::map<TxId, CWalletTx>::const_iterator it = | std::map<TxId, CWalletTx>::const_iterator it = | ||||
mapWallet.find(outpoint.GetTxId()); | mapWallet.find(outpoint.GetTxId()); | ||||
if (it == mapWallet.end()) { | if (it == mapWallet.end()) { | ||||
// TODO: Allow non-wallet inputs | // TODO: Allow non-wallet inputs | ||||
return false; | return false; | ||||
} | } | ||||
const CWalletTx *pcoin = &it->second; | const CWalletTx &wtx = it->second; | ||||
// Clearly invalid input, fail. | // Clearly invalid input, fail. | ||||
if (pcoin->tx->vout.size() <= outpoint.GetN()) { | if (wtx.tx->vout.size() <= outpoint.GetN()) { | ||||
return false; | return false; | ||||
} | } | ||||
// Just to calculate the marginal byte size | // Just to calculate the marginal byte size | ||||
nValueFromPresetInputs += pcoin->tx->vout[outpoint.GetN()].nValue; | nValueFromPresetInputs += wtx.tx->vout[outpoint.GetN()].nValue; | ||||
setPresetCoins.insert(CInputCoin(pcoin->tx, outpoint.GetN())); | setPresetCoins.insert(CInputCoin(wtx.tx, outpoint.GetN())); | ||||
} | } | ||||
// Remove preset inputs from vCoins | // Remove preset inputs from vCoins | ||||
for (std::vector<COutput>::iterator it = vCoins.begin(); | for (std::vector<COutput>::iterator it = vCoins.begin(); | ||||
it != vCoins.end() && coin_control.HasSelected();) { | it != vCoins.end() && coin_control.HasSelected();) { | ||||
if (setPresetCoins.count(it->GetInputCoin())) { | if (setPresetCoins.count(it->GetInputCoin())) { | ||||
it = vCoins.erase(it); | it = vCoins.erase(it); | ||||
} else { | } else { | ||||
▲ Show 20 Lines • Show All 1,105 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
std::map<CTxDestination, Amount> | std::map<CTxDestination, Amount> | ||||
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); | ||||
for (const auto &walletEntry : mapWallet) { | for (const auto &walletEntry : mapWallet) { | ||||
const CWalletTx *pcoin = &walletEntry.second; | const CWalletTx &wtx = walletEntry.second; | ||||
if (!pcoin->IsTrusted(locked_chain)) { | if (!wtx.IsTrusted(locked_chain)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (pcoin->IsImmatureCoinBase(locked_chain)) { | if (wtx.IsImmatureCoinBase(locked_chain)) { | ||||
continue; | continue; | ||||
} | } | ||||
int nDepth = pcoin->GetDepthInMainChain(locked_chain); | int nDepth = wtx.GetDepthInMainChain(locked_chain); | ||||
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) { | if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1)) { | ||||
continue; | continue; | ||||
} | } | ||||
for (uint32_t i = 0; i < pcoin->tx->vout.size(); i++) { | for (uint32_t i = 0; i < wtx.tx->vout.size(); i++) { | ||||
CTxDestination addr; | CTxDestination addr; | ||||
if (!IsMine(pcoin->tx->vout[i])) { | if (!IsMine(wtx.tx->vout[i])) { | ||||
continue; | continue; | ||||
} | } | ||||
if (!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) { | if (!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr)) { | ||||
continue; | continue; | ||||
} | } | ||||
Amount n = IsSpent(locked_chain, COutPoint(walletEntry.first, i)) | Amount n = IsSpent(locked_chain, COutPoint(walletEntry.first, i)) | ||||
? Amount::zero() | ? Amount::zero() | ||||
: pcoin->tx->vout[i].nValue; | : wtx.tx->vout[i].nValue; | ||||
if (!balances.count(addr)) { | if (!balances.count(addr)) { | ||||
balances[addr] = Amount::zero(); | balances[addr] = Amount::zero(); | ||||
} | } | ||||
balances[addr] += n; | balances[addr] += n; | ||||
} | } | ||||
} | } | ||||
return balances; | return balances; | ||||
} | } | ||||
std::set<std::set<CTxDestination>> CWallet::GetAddressGroupings() { | std::set<std::set<CTxDestination>> CWallet::GetAddressGroupings() { | ||||
// mapWallet | // mapWallet | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
std::set<std::set<CTxDestination>> groupings; | std::set<std::set<CTxDestination>> groupings; | ||||
std::set<CTxDestination> grouping; | std::set<CTxDestination> grouping; | ||||
for (const auto &walletEntry : mapWallet) { | for (const auto &walletEntry : mapWallet) { | ||||
const CWalletTx *pcoin = &walletEntry.second; | const CWalletTx &wtx = walletEntry.second; | ||||
if (pcoin->tx->vin.size() > 0) { | if (wtx.tx->vin.size() > 0) { | ||||
bool any_mine = false; | bool any_mine = false; | ||||
// Group all input addresses with each other. | // Group all input addresses with each other. | ||||
for (const auto &txin : pcoin->tx->vin) { | for (const auto &txin : wtx.tx->vin) { | ||||
CTxDestination address; | CTxDestination address; | ||||
// If this input isn't mine, ignore it. | // If this input isn't mine, ignore it. | ||||
if (!IsMine(txin)) { | if (!IsMine(txin)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (!ExtractDestination(mapWallet.at(txin.prevout.GetTxId()) | if (!ExtractDestination(mapWallet.at(txin.prevout.GetTxId()) | ||||
.tx->vout[txin.prevout.GetN()] | .tx->vout[txin.prevout.GetN()] | ||||
.scriptPubKey, | .scriptPubKey, | ||||
address)) { | address)) { | ||||
continue; | continue; | ||||
} | } | ||||
grouping.insert(address); | grouping.insert(address); | ||||
any_mine = true; | any_mine = true; | ||||
} | } | ||||
// Group change with input addresses. | // Group change with input addresses. | ||||
if (any_mine) { | if (any_mine) { | ||||
for (const auto &txout : pcoin->tx->vout) { | for (const auto &txout : wtx.tx->vout) { | ||||
if (IsChange(txout)) { | if (IsChange(txout)) { | ||||
CTxDestination txoutAddr; | CTxDestination txoutAddr; | ||||
if (!ExtractDestination(txout.scriptPubKey, | if (!ExtractDestination(txout.scriptPubKey, | ||||
txoutAddr)) { | txoutAddr)) { | ||||
continue; | continue; | ||||
} | } | ||||
grouping.insert(txoutAddr); | grouping.insert(txoutAddr); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (grouping.size() > 0) { | if (grouping.size() > 0) { | ||||
groupings.insert(grouping); | groupings.insert(grouping); | ||||
grouping.clear(); | grouping.clear(); | ||||
} | } | ||||
} | } | ||||
// Group lone addrs by themselves. | // Group lone addrs by themselves. | ||||
for (const auto &txout : pcoin->tx->vout) { | for (const auto &txout : wtx.tx->vout) { | ||||
if (IsMine(txout)) { | if (IsMine(txout)) { | ||||
CTxDestination address; | CTxDestination address; | ||||
if (!ExtractDestination(txout.scriptPubKey, address)) { | if (!ExtractDestination(txout.scriptPubKey, address)) { | ||||
continue; | continue; | ||||
} | } | ||||
grouping.insert(address); | grouping.insert(address); | ||||
groupings.insert(grouping); | groupings.insert(grouping); | ||||
▲ Show 20 Lines • Show All 983 Lines • Show Last 20 Lines |