diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index fad7e28265..ab2065c7e7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -279,6 +279,22 @@ unsigned int CTransparentTxout::GetDepthInMainChain() const { return wallet->mapWallet.at(GetHash()).GetDepthInMainChain(); } +unsigned int CTransparentTxout::GetDepthInMempool() const { + if (_isMockup) + return GetDepthInMainChain() ? 0 : 1; + + assert(wallet); + AssertLockHeld(wallet->cs_wallet); + AssertLockHeld(mempool.cs); + + auto entry = mempool.mapTx.find(GetHash()); + if (entry == mempool.mapTx.end()) + return 0; + + uint64_t nAncestors = entry->GetCountWithAncestors(); + return nAncestors; +} + const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); /** @defgroup mapWallet @@ -4627,7 +4643,7 @@ void CWallet::CheckTransparentTransactionSanity(CMutableTransaction& tx, throw std::runtime_error("txFee > maxTxFee. This is probably a bug."); bool fHasDataOut = false; - for (CTxOut& txout: tx.vout) { + for (const CTxOut& txout: tx.vout) { txnouttype whichType; if (!::IsStandard(txout.scriptPubKey, whichType, false)) @@ -4663,6 +4679,18 @@ void CWallet::CheckTransparentTransactionSanity(CMutableTransaction& tx, for (CTxOut& txout: tx.vout) totalOutputValue += txout.nValue; assert(totalInputValue == totalOutputValue + nFee); assert(totalInputValue > 0); + + if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { + // We should probably not make these transactions in the first place, but throwing this exception instead is the + // backwards compatible behaviour. + size_t nTotalMempoolAncestors = 0; + + for (const CTransparentTxout& txin: vInputTxs) + nTotalMempoolAncestors += txin.GetDepthInMempool(); + + if (nTotalMempoolAncestors >= GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)) + throw std::runtime_error("mempool chain"); + } } // Create a transaction to vecSend, placing it in wtxNew. reservekey is the keypool. nFeeRet will be populated with the @@ -4692,6 +4720,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign, int nExtraPayloadSize, bool fUseInstantSend, const std::vector& vTransparentTxouts) { + LOCK(mempool.cs); AssertLockHeld(cs_main); if (!coinControl || coinControl->destChange.which() == 0) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index aede7ea9d4..d850c2488b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -14,6 +14,7 @@ #include "tinyformat.h" #include "ui_interface.h" #include "utilstrencodings.h" +#include "validation.h" #include "validationinterface.h" #include "script/ismine.h" #include "script/sign.h" @@ -677,6 +678,7 @@ class CTransparentTxout { bool IsCoinBase() const; bool IsFromMe() const; unsigned int GetDepthInMainChain() const; + unsigned int GetDepthInMempool() const; private: const CWallet* wallet = nullptr; @@ -747,6 +749,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface if (coinControl && coinControl->nCoinType == CoinType::WITH_MINTS) throw std::runtime_error("CoinType::WITH_MINTS is not supported for any transactions."); + unsigned int nMaxMempoolDepth = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); + for (const AbstractTxout& tx: vRelevantTransactions) { bool isSelected = coinControl && coinControl->HasSelected() && coinControl->IsSelected(tx.GetOutpoint()); @@ -765,6 +770,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface (!fUseInstantSend || !tx.IsLLMQInstantSendLocked()) && ((coinControl && !coinControl->fAllowUnconfirmed) || !tx.IsFromMe()) ) continue; + if (fRejectLongChains && tx.GetDepthInMempool() + 1 >= nMaxMempoolDepth) continue; if (isSelected) vCoinControlInputs.push_back(tx); else vAvailableInputs.push_back(tx); @@ -812,6 +818,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface nCollectedRet = 0; size_t nMaxSize = coinControl && coinControl->nMaxSize ? coinControl->nMaxSize : (MAX_STANDARD_TX_WEIGHT / 4); + size_t nMaxAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); if (nRequired < 0) throw std::runtime_error("Transaction amounts must be positive");