|
7 | 7 | #include <chainparams.h>
|
8 | 8 | #include <node/context.h>
|
9 | 9 | #include <node/mempool_args.h>
|
| 10 | +#include <policy/rbf.h> |
10 | 11 | #include <policy/v3_policy.h>
|
11 | 12 | #include <txmempool.h>
|
12 | 13 | #include <util/check.h>
|
@@ -68,6 +69,28 @@ std::optional<std::string> CheckPackageMempoolAcceptResult(const Package& txns,
|
68 | 69 | return strprintf("tx %s unexpectedly failed: %s", wtxid.ToString(), atmp_result.m_state.ToString());
|
69 | 70 | }
|
70 | 71 |
|
| 72 | + // Each subpackage is allowed MAX_REPLACEMENT_CANDIDATES replacements (only checking individually here) |
| 73 | + if (atmp_result.m_replaced_transactions.size() > MAX_REPLACEMENT_CANDIDATES) { |
| 74 | + return strprintf("tx %s result replaced too many transactions", |
| 75 | + wtxid.ToString()); |
| 76 | + } |
| 77 | + |
| 78 | + // Replacements can't happen for subpackages larger than 2 |
| 79 | + if (!atmp_result.m_replaced_transactions.empty() && |
| 80 | + atmp_result.m_wtxids_fee_calculations.has_value() && atmp_result.m_wtxids_fee_calculations.value().size() > 2) { |
| 81 | + return strprintf("tx %s was part of a too-large package RBF subpackage", |
| 82 | + wtxid.ToString()); |
| 83 | + } |
| 84 | + |
| 85 | + if (!atmp_result.m_replaced_transactions.empty() && mempool) { |
| 86 | + LOCK(mempool->cs); |
| 87 | + // If replacements occurred and it used 2 transactions, this is a package RBF and should result in a cluster of size 2 |
| 88 | + if (atmp_result.m_wtxids_fee_calculations.has_value() && atmp_result.m_wtxids_fee_calculations.value().size() == 2) { |
| 89 | + const auto cluster = mempool->GatherClusters({tx->GetHash()}); |
| 90 | + if (cluster.size() != 2) return strprintf("tx %s has too many ancestors or descendants for a package rbf", wtxid.ToString()); |
| 91 | + } |
| 92 | + } |
| 93 | + |
71 | 94 | // m_vsize and m_base_fees should exist iff the result was VALID or MEMPOOL_ENTRY
|
72 | 95 | const bool mempool_entry{atmp_result.m_result_type == MempoolAcceptResult::ResultType::MEMPOOL_ENTRY};
|
73 | 96 | if (atmp_result.m_base_fees.has_value() != (valid || mempool_entry)) {
|
@@ -108,6 +131,11 @@ std::optional<std::string> CheckPackageMempoolAcceptResult(const Package& txns,
|
108 | 131 | return strprintf("wtxid %s should not be in mempool", wtxid.ToString());
|
109 | 132 | }
|
110 | 133 | }
|
| 134 | + for (const auto& tx_ref : atmp_result.m_replaced_transactions) { |
| 135 | + if (mempool->exists(GenTxid::Txid(tx_ref->GetHash()))) { |
| 136 | + return strprintf("tx %s should not be in mempool as it was replaced", tx_ref->GetWitnessHash().ToString()); |
| 137 | + } |
| 138 | + } |
111 | 139 | }
|
112 | 140 | }
|
113 | 141 | return std::nullopt;
|
|
0 commit comments