mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 23:07:59 +01:00
wallet: enable avoid_reuse feature
This commit is contained in:
parent
eec15662fa
commit
8247a0da3a
3 changed files with 65 additions and 14 deletions
|
@ -20,7 +20,9 @@ enum isminetype
|
||||||
ISMINE_NO = 0,
|
ISMINE_NO = 0,
|
||||||
ISMINE_WATCH_ONLY = 1 << 0,
|
ISMINE_WATCH_ONLY = 1 << 0,
|
||||||
ISMINE_SPENDABLE = 1 << 1,
|
ISMINE_SPENDABLE = 1 << 1,
|
||||||
|
ISMINE_USED = 1 << 2,
|
||||||
ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE,
|
ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE,
|
||||||
|
ISMINE_ALL_USED = ISMINE_ALL | ISMINE_USED,
|
||||||
ISMINE_ENUM_ELEMENTS,
|
ISMINE_ENUM_ELEMENTS,
|
||||||
};
|
};
|
||||||
/** used for bitflags of isminetype */
|
/** used for bitflags of isminetype */
|
||||||
|
|
|
@ -945,6 +945,37 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
|
||||||
return success;
|
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)
|
bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
|
||||||
{
|
{
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
|
@ -953,6 +984,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
|
||||||
|
|
||||||
uint256 hash = wtxIn.GetHash();
|
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
|
// 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));
|
std::pair<std::map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(std::make_pair(hash, wtxIn));
|
||||||
CWalletTx& wtx = (*ret.first).second;
|
CWalletTx& wtx = (*ret.first).second;
|
||||||
|
@ -2072,7 +2111,7 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
|
// 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
|
// Must wait until coinbase is safely deep enough in the chain before valuing it
|
||||||
if (IsImmatureCoinBase(locked_chain))
|
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];
|
return m_amounts[AVAILABLE_CREDIT].m_value[filter];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
|
||||||
CAmount nCredit = 0;
|
CAmount nCredit = 0;
|
||||||
uint256 hashTx = GetHash();
|
uint256 hashTx = GetHash();
|
||||||
for (unsigned int i = 0; i < tx->vout.size(); i++)
|
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];
|
const CTxOut &txout = tx->vout[i];
|
||||||
nCredit += pwallet->GetCredit(txout, filter);
|
nCredit += pwallet->GetCredit(txout, filter);
|
||||||
if (!MoneyRange(nCredit))
|
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;
|
Balance ret;
|
||||||
|
isminefilter reuse_filter = avoid_reuse ? 0 : ISMINE_USED;
|
||||||
{
|
{
|
||||||
auto locked_chain = chain().lock();
|
auto locked_chain = chain().lock();
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
|
@ -2240,8 +2280,8 @@ CWallet::Balance CWallet::GetBalance(const int min_depth) const
|
||||||
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 CAmount tx_credit_mine{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_SPENDABLE)};
|
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)};
|
const CAmount 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;
|
||||||
|
@ -2279,6 +2319,9 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
|
||||||
|
|
||||||
vCoins.clear();
|
vCoins.clear();
|
||||||
CAmount nTotal = 0;
|
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)
|
for (const auto& entry : mapWallet)
|
||||||
{
|
{
|
||||||
|
@ -2360,6 +2403,10 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
|
||||||
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 = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
|
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
|
// ensure this wallet.dat can only be opened by clients supporting 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 & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) {
|
||||||
walletInstance->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
|
||||||
} 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() && !walletInstance->TopUpKeyPool()) {
|
if (walletInstance->CanGenerateKeys() && !walletInstance->TopUpKeyPool()) {
|
||||||
|
|
|
@ -941,6 +941,12 @@ public:
|
||||||
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
|
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);
|
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;
|
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);
|
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_untrusted_pending{0};
|
||||||
CAmount m_watchonly_immature{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;
|
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
|
||||||
|
|
||||||
OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);
|
OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);
|
||||||
|
|
Loading…
Add table
Reference in a new issue