diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/script/ismine.h | 2 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 69 | ||||
-rw-r--r-- | src/wallet/wallet.h | 8 |
3 files changed, 65 insertions, 14 deletions
diff --git a/src/script/ismine.h b/src/script/ismine.h index 55e28e225a..da3da7e324 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -20,7 +20,9 @@ enum isminetype ISMINE_NO = 0, ISMINE_WATCH_ONLY = 1 << 0, ISMINE_SPENDABLE = 1 << 1, + ISMINE_USED = 1 << 2, ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE, + ISMINE_ALL_USED = ISMINE_ALL | ISMINE_USED, ISMINE_ENUM_ELEMENTS, }; /** used for bitflags of isminetype */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2c51b6a8ba..6ac1a42bf5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -945,6 +945,37 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) return success; } +void CWallet::SetUsedDestinationState(const uint256& 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)) { + AddDestData(dst, "used", "p"); // p for "present", opposite of absent (null) + } 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 uint256& hash, unsigned int n) const +{ + CTxDestination dst; + const CWalletTx* srctx = GetWalletTx(hash); + return srctx && ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst) && IsUsedDestination(dst); +} + bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) { LOCK(cs_wallet); @@ -953,6 +984,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) uint256 hash = wtxIn.GetHash(); + if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) { + // Mark used destinations + for (const CTxIn& txin : wtxIn.tx->vin) { + const COutPoint& op = txin.prevout; + SetUsedDestinationState(op.hash, op.n, true); + } + } + // Inserts only if not already there, returns tx inserted or tx found std::pair<std::map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(std::make_pair(hash, wtxIn)); CWalletTx& wtx = (*ret.first).second; @@ -2072,7 +2111,7 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo return 0; // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future). - bool allow_cache = filter == ISMINE_SPENDABLE || filter == ISMINE_WATCH_ONLY; + bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL; // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsImmatureCoinBase(locked_chain)) @@ -2082,12 +2121,12 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo return m_amounts[AVAILABLE_CREDIT].m_value[filter]; } + bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE); CAmount nCredit = 0; uint256 hashTx = GetHash(); for (unsigned int i = 0; i < tx->vout.size(); i++) { - if (!pwallet->IsSpent(locked_chain, hashTx, i)) - { + if (!pwallet->IsSpent(locked_chain, hashTx, i) && (allow_used_addresses || !pwallet->IsUsedDestination(hashTx, i))) { const CTxOut &txout = tx->vout[i]; nCredit += pwallet->GetCredit(txout, filter); if (!MoneyRange(nCredit)) @@ -2229,9 +2268,10 @@ void MaybeResendWalletTxs() */ -CWallet::Balance CWallet::GetBalance(const int min_depth) const +CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) const { Balance ret; + isminefilter reuse_filter = avoid_reuse ? 0 : ISMINE_USED; { auto locked_chain = chain().lock(); LOCK(cs_wallet); @@ -2240,8 +2280,8 @@ CWallet::Balance CWallet::GetBalance(const int min_depth) const const CWalletTx& wtx = entry.second; const bool is_trusted{wtx.IsTrusted(*locked_chain)}; const int tx_depth{wtx.GetDepthInMainChain(*locked_chain)}; - const CAmount tx_credit_mine{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_SPENDABLE)}; - const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_WATCH_ONLY)}; + const CAmount tx_credit_mine{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)}; + const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)}; if (is_trusted && tx_depth >= min_depth) { ret.m_mine_trusted += tx_credit_mine; ret.m_watchonly_trusted += tx_credit_watchonly; @@ -2279,6 +2319,9 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector< vCoins.clear(); CAmount nTotal = 0; + // 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); for (const auto& entry : mapWallet) { @@ -2360,6 +2403,10 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector< continue; } + if (!allow_used_addresses && IsUsedDestination(wtxid, i)) { + continue; + } + bool solvable = IsSolvable(*this, wtx.tx->vout[i].scriptPubKey); bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable)); @@ -4150,16 +4197,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key walletInstance->SetMinVersion(FEATURE_LATEST); - if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { - //selective allow to set flags - walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS); - } else if (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET) { - walletInstance->SetWalletFlag(WALLET_FLAG_BLANK_WALLET); - } else { + walletInstance->SetWalletFlags(wallet_creation_flags, false); + if (!(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) { // generate a new seed CPubKey seed = walletInstance->GenerateNewSeed(); walletInstance->SetHDSeed(seed); - } // Otherwise, do not generate a new seed + } // Top up the keypool if (walletInstance->CanGenerateKeys() && !walletInstance->TopUpKeyPool()) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4ff347a596..311a5030cc 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -941,6 +941,12 @@ public: std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const; bool IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + + // Whether this or any UTXO with the same CTxDestination has been spent. + bool IsUsedDestination(const CTxDestination& dst) const; + bool IsUsedDestination(const uint256& hash, unsigned int n) const; + void SetUsedDestinationState(const uint256& hash, unsigned int n, bool used); + std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const; bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -1053,7 +1059,7 @@ public: CAmount m_watchonly_untrusted_pending{0}; CAmount m_watchonly_immature{0}; }; - Balance GetBalance(int min_depth = 0) const; + Balance GetBalance(int min_depth = 0, bool avoid_reuse = true) const; CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const; OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend); |