|
|
|
@ -451,6 +451,11 @@ public:
|
|
|
|
|
* any transaction spending the same inputs as a transaction in the mempool is considered
|
|
|
|
|
* a conflict. */
|
|
|
|
|
const bool m_allow_bip125_replacement;
|
|
|
|
|
/** When true, the mempool will not be trimmed when individual transactions are submitted in
|
|
|
|
|
* Finalize(). Instead, limits should be enforced at the end to ensure the package is not
|
|
|
|
|
* partially submitted.
|
|
|
|
|
*/
|
|
|
|
|
const bool m_package_submission;
|
|
|
|
|
|
|
|
|
|
/** Parameters for single transaction mempool validation. */
|
|
|
|
|
static ATMPArgs SingleAccept(const CChainParams& chainparams, int64_t accept_time,
|
|
|
|
@ -462,6 +467,7 @@ public:
|
|
|
|
|
/* m_coins_to_uncache */ coins_to_uncache,
|
|
|
|
|
/* m_test_accept */ test_accept,
|
|
|
|
|
/* m_allow_bip125_replacement */ true,
|
|
|
|
|
/* m_package_submission */ false,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -474,6 +480,7 @@ public:
|
|
|
|
|
/* m_coins_to_uncache */ coins_to_uncache,
|
|
|
|
|
/* m_test_accept */ true,
|
|
|
|
|
/* m_allow_bip125_replacement */ false,
|
|
|
|
|
/* m_package_submission */ false, // not submitting to mempool
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -486,6 +493,7 @@ public:
|
|
|
|
|
/* m_coins_to_uncache */ coins_to_uncache,
|
|
|
|
|
/* m_test_accept */ false,
|
|
|
|
|
/* m_allow_bip125_replacement */ false,
|
|
|
|
|
/* m_package_submission */ true,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
// No default ctor to avoid exposing details to clients and allowing the possibility of
|
|
|
|
@ -497,9 +505,9 @@ public:
|
|
|
|
|
MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Multiple transaction acceptance. Transactions may or may not be interdependent,
|
|
|
|
|
* but must not conflict with each other. Parents must come before children if any
|
|
|
|
|
* dependencies exist.
|
|
|
|
|
* Multiple transaction acceptance. Transactions may or may not be interdependent, but must not
|
|
|
|
|
* conflict with each other, and the transactions cannot already be in the mempool. Parents must
|
|
|
|
|
* come before children if any dependencies exist.
|
|
|
|
|
*/
|
|
|
|
|
PackageMempoolAcceptResult AcceptMultipleTransactions(const std::vector<CTransactionRef>& txns, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
|
|
|
|
|
|
|
@ -581,6 +589,14 @@ private:
|
|
|
|
|
// limiting is performed, false otherwise.
|
|
|
|
|
bool Finalize(const ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
|
|
|
|
|
|
|
|
|
// Submit all transactions to the mempool and call ConsensusScriptChecks to add to the script
|
|
|
|
|
// cache - should only be called after successful validation of all transactions in the package.
|
|
|
|
|
// The package may end up partially-submitted after size limitting; returns true if all
|
|
|
|
|
// transactions are successfully added to the mempool, false otherwise.
|
|
|
|
|
bool FinalizePackage(const ATMPArgs& args, std::vector<Workspace>& workspaces, PackageValidationState& package_state,
|
|
|
|
|
std::map<const uint256, const MempoolAcceptResult>& results)
|
|
|
|
|
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
|
|
|
|
|
|
|
|
|
// Compare a package's feerate against minimum allowed.
|
|
|
|
|
bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs)
|
|
|
|
|
{
|
|
|
|
@ -992,13 +1008,18 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
|
|
|
|
|
// - it's not being re-added during a reorg which bypasses typical mempool fee limits
|
|
|
|
|
// - the node is not behind
|
|
|
|
|
// - the transaction is not dependent on any other transactions in the mempool
|
|
|
|
|
bool validForFeeEstimation = !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
|
|
|
|
|
// - it's not part of a package. Since package relay is not currently supported, this
|
|
|
|
|
// transaction has not necessarily been accepted to miners' mempools.
|
|
|
|
|
bool validForFeeEstimation = !bypass_limits && !args.m_package_submission && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
|
|
|
|
|
|
|
|
|
|
// Store transaction in memory
|
|
|
|
|
m_pool.addUnchecked(*entry, ws.m_ancestors, validForFeeEstimation);
|
|
|
|
|
|
|
|
|
|
// trim mempool and check if tx was trimmed
|
|
|
|
|
if (!bypass_limits) {
|
|
|
|
|
// If we are validating a package, don't trim here because we could evict a previous transaction
|
|
|
|
|
// in the package. LimitMempoolSize() should be called at the very end to make sure the mempool
|
|
|
|
|
// is still within limits and package submission happens atomically.
|
|
|
|
|
if (!args.m_package_submission && !bypass_limits) {
|
|
|
|
|
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
|
|
|
|
|
if (!m_pool.exists(GenTxid::Txid(hash)))
|
|
|
|
|
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
|
|
|
|
@ -1006,6 +1027,69 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MemPoolAccept::FinalizePackage(const ATMPArgs& args, std::vector<Workspace>& workspaces,
|
|
|
|
|
PackageValidationState& package_state,
|
|
|
|
|
std::map<const uint256, const MempoolAcceptResult>& results)
|
|
|
|
|
{
|
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
AssertLockHeld(m_pool.cs);
|
|
|
|
|
bool all_submitted = true;
|
|
|
|
|
// ConsensusScriptChecks adds to the script cache and is therefore consensus-critical;
|
|
|
|
|
// CheckInputsFromMempoolAndCache asserts that transactions only spend coins available from the
|
|
|
|
|
// mempool or UTXO set. Submit each transaction to the mempool immediately after calling
|
|
|
|
|
// ConsensusScriptChecks to make the outputs available for subsequent transactions.
|
|
|
|
|
for (Workspace& ws : workspaces) {
|
|
|
|
|
if (!ConsensusScriptChecks(args, ws)) {
|
|
|
|
|
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
|
|
|
|
|
// Since PolicyScriptChecks() passed, this should never fail.
|
|
|
|
|
all_submitted = Assume(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Re-calculate mempool ancestors to call addUnchecked(). They may have changed since the
|
|
|
|
|
// last calculation done in PreChecks, since package ancestors have already been submitted.
|
|
|
|
|
std::string err_string;
|
|
|
|
|
if(!m_pool.CalculateMemPoolAncestors(*ws.m_entry, ws.m_ancestors, m_limit_ancestors,
|
|
|
|
|
m_limit_ancestor_size, m_limit_descendants,
|
|
|
|
|
m_limit_descendant_size, err_string)) {
|
|
|
|
|
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
|
|
|
|
|
// Since PreChecks() and PackageMempoolChecks() both enforce limits, this should never fail.
|
|
|
|
|
all_submitted = Assume(false);
|
|
|
|
|
}
|
|
|
|
|
// If we call LimitMempoolSize() for each individual Finalize(), the mempool will not take
|
|
|
|
|
// the transaction's descendant feerate into account because it hasn't seen them yet. Also,
|
|
|
|
|
// we risk evicting a transaction that a subsequent package transaction depends on. Instead,
|
|
|
|
|
// allow the mempool to temporarily bypass limits, the maximum package size) while
|
|
|
|
|
// submitting transactions individually and then trim at the very end.
|
|
|
|
|
if (!Finalize(args, ws)) {
|
|
|
|
|
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
|
|
|
|
|
// Since LimitMempoolSize() won't be called, this should never fail.
|
|
|
|
|
all_submitted = Assume(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// It may or may not be the case that all the transactions made it into the mempool. Regardless,
|
|
|
|
|
// make sure we haven't exceeded max mempool size.
|
|
|
|
|
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(),
|
|
|
|
|
gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
|
|
|
|
|
std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
|
|
|
|
|
if (!all_submitted) return false;
|
|
|
|
|
|
|
|
|
|
// Find the wtxids of the transactions that made it into the mempool. Allow partial submission,
|
|
|
|
|
// but don't report success unless they all made it into the mempool.
|
|
|
|
|
for (Workspace& ws : workspaces) {
|
|
|
|
|
if (m_pool.exists(GenTxid::Wtxid(ws.m_ptx->GetWitnessHash()))) {
|
|
|
|
|
results.emplace(ws.m_ptx->GetWitnessHash(),
|
|
|
|
|
MempoolAcceptResult::Success(std::move(ws.m_replaced_transactions), ws.m_vsize, ws.m_base_fees));
|
|
|
|
|
GetMainSignals().TransactionAddedToMempool(ws.m_ptx, m_pool.GetAndIncrementSequence());
|
|
|
|
|
} else {
|
|
|
|
|
all_submitted = false;
|
|
|
|
|
ws.m_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
|
|
|
|
|
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return all_submitted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
|
|
|
|
|
{
|
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
@ -1091,6 +1175,13 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.m_test_accept) return PackageMempoolAcceptResult(package_state, std::move(results));
|
|
|
|
|
|
|
|
|
|
if (!FinalizePackage(args, workspaces, package_state, results)) {
|
|
|
|
|
package_state.Invalid(PackageValidationResult::PCKG_TX, "submission failed");
|
|
|
|
|
return PackageMempoolAcceptResult(package_state, std::move(results));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PackageMempoolAcceptResult(package_state, std::move(results));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1187,7 +1278,6 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
|
|
|
|
|
const Package& package, bool test_accept)
|
|
|
|
|
{
|
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
assert(test_accept); // Only allow package accept dry-runs (testmempoolaccept RPC).
|
|
|
|
|
assert(!package.empty());
|
|
|
|
|
assert(std::all_of(package.cbegin(), package.cend(), [](const auto& tx){return tx != nullptr;}));
|
|
|
|
|
|
|
|
|
@ -1205,9 +1295,14 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
// Uncache coins pertaining to transactions that were not submitted to the mempool.
|
|
|
|
|
for (const COutPoint& hashTx : coins_to_uncache) {
|
|
|
|
|
active_chainstate.CoinsTip().Uncache(hashTx);
|
|
|
|
|
// Ensure the coins cache is still within limits.
|
|
|
|
|
if (test_accept || result.m_state.IsInvalid()) {
|
|
|
|
|
for (const COutPoint& hashTx : coins_to_uncache) {
|
|
|
|
|
active_chainstate.CoinsTip().Uncache(hashTx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BlockValidationState state_dummy;
|
|
|
|
|
active_chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|