diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -19,8 +19,8 @@ class CBlock; struct CBlockLocator; class CChainParams; +class Config; class CScheduler; -class CTransaction; class CValidationState; namespace Consensus { struct Params; @@ -119,6 +119,12 @@ virtual bool contextualCheckTransactionForCurrentBlock( const Consensus::Params ¶ms, const CTransaction &tx, CValidationState &state) = 0; + + //! Add transaction to memory pool if the transaction fee is below the + //! amount specified by absurd_fee (as a safeguard). */ + virtual bool submitToMemoryPool(const Config &config, + CTransactionRef tx, Amount absurd_fee, + CValidationState &state) = 0; }; //! Return Lock interface. Chain is locked when this is called, and @@ -158,6 +164,12 @@ //! Check chain limits. virtual bool checkChainLimits(CTransactionRef tx) = 0; + //! Get node max tx fee setting (-maxtxfee). + //! This could be replaced by a per-wallet max fee, as proposed at + //! https://github.com/bitcoin/bitcoin/issues/15355 + //! But for the time being, wallets call this to access the node setting. + virtual Amount maxTxFee() = 0; + //! Check if pruning is enabled. virtual bool getPruneMode() = 0; diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -146,6 +147,14 @@ LockAnnotation lock(::cs_main); return ContextualCheckTransactionForCurrentBlock(params, tx, state); } + bool submitToMemoryPool(const Config &config, CTransactionRef tx, + Amount absurd_fee, + CValidationState &state) override { + LockAnnotation lock(::cs_main); + return AcceptToMemoryPool(config, ::g_mempool, state, tx, + nullptr /* missing inputs */, + false /* bypass limits */, absurd_fee); + } }; class LockingStateImpl : public LockImpl, @@ -231,6 +240,7 @@ limit_descendant_count, limit_descendant_size, unused_error_string); } + Amount maxTxFee() override { return ::maxTxFee; } bool getPruneMode() override { return ::fPruneMode; } bool p2pEnabled() override { return g_connman != nullptr; } int64_t getAdjustedTime() override { return GetAdjustedTime(); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -556,7 +556,7 @@ * absurd fee. */ bool AcceptToMemoryPool(interfaces::Chain::Lock &locked_chain, - const Amount nAbsurdFee, CValidationState &state); + CValidationState &state); // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1989,7 +1989,7 @@ for (const std::pair &item : mapSorted) { CWalletTx &wtx = *(item.second); CValidationState state; - wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state); + wtx.AcceptToMemoryPool(*locked_chain, state); } } @@ -2002,7 +2002,7 @@ CValidationState state; // GetDepthInMainChain already catches known conflicts. - if (InMempool() || AcceptToMemoryPool(locked_chain, maxTxFee, state)) { + if (InMempool() || AcceptToMemoryPool(locked_chain, state)) { pwallet->WalletLogPrintf("Relaying wtx %s\n", GetId().ToString()); if (pwallet->chain().p2pEnabled()) { pwallet->chain().relayTransaction(GetId()); @@ -3441,7 +3441,7 @@ if (fBroadcastTransactions) { // Broadcast - if (!wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state)) { + if (!wtx.AcceptToMemoryPool(*locked_chain, state)) { WalletLogPrintf("CommitTransaction(): Transaction cannot be " "broadcast immediately, %s\n", FormatStateMessage(state)); @@ -4864,19 +4864,14 @@ } bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock &locked_chain, - const Amount nAbsurdFee, CValidationState &state) { - // Temporary, for AcceptToMemoryPool below. Removed in upcoming commit. - LockAnnotation lock(::cs_main); - // We must set fInMempool here - while it will be re-set to true by the // entered-mempool callback, if we did not there would be a race where a // user could call sendmoney in a loop and hit spurious out of funds errors // because we think that this newly generated transaction's change is // unavailable as we're not yet aware that it is in the mempool. - bool ret = ::AcceptToMemoryPool(GetConfig(), g_mempool, state, tx, - nullptr /* pfMissingInputs */, - false /* bypass_limits */, nAbsurdFee); + bool ret = locked_chain.submitToMemoryPool( + ::GetConfig(), tx, pwallet->chain().maxTxFee(), state); fInMempool |= ret; return ret; }