mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-05-02 16:01:58 +02:00
[policy] disallow transactions under min relay fee, even in packages
Avoid adding transactions below min relay feerate because, even if they were bumped through CPFP when entering the mempool, we do not have a DoS-resistant way of ensuring they always remain bumped. In the future, this rule can be relaxed (e.g. to allow packages to bump 0-fee transactions) if we find a way to do so.
This commit is contained in:
parent
c4554fe894
commit
563a2ee4f5
@ -80,24 +80,37 @@ test accepts):
|
||||
If any transactions in the package are already in the mempool, they are not submitted again
|
||||
("deduplicated") and are thus excluded from this calculation.
|
||||
|
||||
To meet the two feerate requirements of a mempool, i.e., the pre-configured minimum relay feerate
|
||||
(`-minrelaytxfee`) and the dynamic mempool minimum feerate, the total package feerate is used instead
|
||||
of the individual feerate. The individual transactions are allowed to be below the feerate
|
||||
requirements if the package meets the feerate requirements. For example, the parent(s) in the
|
||||
package can pay no fees but be paid for by the child.
|
||||
To meet the dynamic mempool minimum feerate, i.e., the feerate determined by the transactions
|
||||
evicted when the mempool reaches capacity (not the static minimum relay feerate), the total package
|
||||
feerate instead of individual feerate can be used. For example, if the mempool minimum feerate is
|
||||
5sat/vB and a 1sat/vB parent transaction has a high-feerate child, it may be accepted if
|
||||
submitted as a package.
|
||||
|
||||
*Rationale*: This can be thought of as "CPFP within a package," solving the issue of a parent not
|
||||
meeting minimum fees on its own. This would allow contracting applications to adjust their fees at
|
||||
broadcast time instead of overshooting or risking becoming stuck or pinned.
|
||||
*Rationale*: This can be thought of as "CPFP within a package," solving the issue of a presigned
|
||||
transaction (i.e. in which a replacement transaction with a higher fee cannot be signed) being
|
||||
rejected from the mempool when transaction volume is high and the mempool minimum feerate rises.
|
||||
|
||||
*Rationale*: It would be incorrect to use the fees of transactions that are already in the mempool, as
|
||||
we do not want a transaction's fees to be double-counted.
|
||||
Note: Package feerate cannot be used to meet the minimum relay feerate (`-minrelaytxfee`)
|
||||
requirement. For example, if the mempool minimum feerate is 5sat/vB and the minimum relay feerate is
|
||||
set to 5satvB, a 1sat/vB parent transaction with a high-feerate child will not be accepted, even if
|
||||
submitted as a package.
|
||||
|
||||
*Rationale*: Avoid situations in which the mempool contains non-bumped transactions below min relay
|
||||
feerate (which we consider to have pay 0 fees and thus receiving free relay). While package
|
||||
submission would ensure these transactions are bumped at the time of entry, it is not guaranteed
|
||||
that the transaction will always be bumped. For example, a later transaction could replace the
|
||||
fee-bumping child without still bumping the parent. These no-longer-bumped transactions should be
|
||||
removed during a replacement, but we do not have a DoS-resistant way of removing them or enforcing a
|
||||
limit on their quantity. Instead, prevent their entry into the mempool.
|
||||
|
||||
Implementation Note: Transactions within a package are always validated individually first, and
|
||||
package validation is used for the transactions that failed. Since package feerate is only
|
||||
calculated using transactions that are not in the mempool, this implementation detail affects the
|
||||
outcome of package validation.
|
||||
|
||||
*Rationale*: It would be incorrect to use the fees of transactions that are already in the mempool, as
|
||||
we do not want a transaction's fees to be double-counted.
|
||||
|
||||
*Rationale*: Packages are intended for incentive-compatible fee-bumping: transaction B is a
|
||||
"legitimate" fee-bump for transaction A only if B is a descendant of A and has a *higher* feerate
|
||||
than A. We want to prevent "parents pay for children" behavior; fees of parents should not help
|
||||
|
@ -671,10 +671,13 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
|
||||
const auto submit_cpfp_deprio = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,
|
||||
package_cpfp, /*test_accept=*/ false);
|
||||
BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
|
||||
BOOST_CHECK_MESSAGE(submit_cpfp_deprio.m_state.IsInvalid(),
|
||||
"Package validation unexpectedly succeeded: " << submit_cpfp_deprio.m_state.GetRejectReason());
|
||||
BOOST_CHECK(submit_cpfp_deprio.m_tx_results.empty());
|
||||
BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_state.GetResult(), PackageValidationResult::PCKG_TX);
|
||||
BOOST_CHECK(submit_cpfp_deprio.m_state.IsInvalid());
|
||||
BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_tx_results.find(tx_parent->GetWitnessHash())->second.m_state.GetResult(),
|
||||
TxValidationResult::TX_MEMPOOL_POLICY);
|
||||
BOOST_CHECK_EQUAL(submit_cpfp_deprio.m_tx_results.find(tx_child->GetWitnessHash())->second.m_state.GetResult(),
|
||||
TxValidationResult::TX_MISSING_INPUTS);
|
||||
BOOST_CHECK(submit_cpfp_deprio.m_tx_results.find(tx_parent->GetWitnessHash())->second.m_state.GetRejectReason() == "min relay fee not met");
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
|
||||
const CFeeRate expected_feerate(0, GetVirtualTransactionSize(*tx_parent) + GetVirtualTransactionSize(*tx_child));
|
||||
}
|
||||
@ -808,8 +811,8 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
|
||||
BOOST_CHECK_MESSAGE(submit_rich_parent.m_state.IsInvalid(), "Package validation unexpectedly succeeded");
|
||||
|
||||
// The child would have been validated on its own and failed, then submitted as a "package" of 1.
|
||||
BOOST_CHECK_EQUAL(submit_rich_parent.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
|
||||
BOOST_CHECK_EQUAL(submit_rich_parent.m_state.GetRejectReason(), "package-fee-too-low");
|
||||
BOOST_CHECK_EQUAL(submit_rich_parent.m_state.GetResult(), PackageValidationResult::PCKG_TX);
|
||||
BOOST_CHECK_EQUAL(submit_rich_parent.m_state.GetRejectReason(), "transaction failed");
|
||||
|
||||
auto it_parent = submit_rich_parent.m_tx_results.find(tx_parent_rich->GetWitnessHash());
|
||||
BOOST_CHECK(it_parent != submit_rich_parent.m_tx_results.end());
|
||||
@ -818,6 +821,11 @@ BOOST_FIXTURE_TEST_CASE(package_cpfp_tests, TestChain100Setup)
|
||||
BOOST_CHECK_MESSAGE(it_parent->second.m_base_fees.value() == high_parent_fee,
|
||||
strprintf("rich parent: expected fee %s, got %s", high_parent_fee, it_parent->second.m_base_fees.value()));
|
||||
BOOST_CHECK(it_parent->second.m_effective_feerate == CFeeRate(high_parent_fee, GetVirtualTransactionSize(*tx_parent_rich)));
|
||||
auto it_child = submit_rich_parent.m_tx_results.find(tx_child_poor->GetWitnessHash());
|
||||
BOOST_CHECK(it_child != submit_rich_parent.m_tx_results.end());
|
||||
BOOST_CHECK_EQUAL(it_child->second.m_result_type, MempoolAcceptResult::ResultType::INVALID);
|
||||
BOOST_CHECK_EQUAL(it_child->second.m_state.GetResult(), TxValidationResult::TX_MEMPOOL_POLICY);
|
||||
BOOST_CHECK(it_child->second.m_state.GetRejectReason() == "min relay fee not met");
|
||||
|
||||
BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);
|
||||
BOOST_CHECK(m_node.mempool->exists(GenTxid::Txid(tx_parent_rich->GetHash())));
|
||||
|
@ -844,9 +844,18 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
|
||||
strprintf("%d", nSigOpsCost));
|
||||
|
||||
// No individual transactions are allowed below the min relay feerate and mempool min feerate except from
|
||||
// disconnected blocks and transactions in a package. Package transactions will be checked using
|
||||
// package feerate later.
|
||||
// No individual transactions are allowed below the min relay feerate except from disconnected blocks.
|
||||
// This requirement, unlike CheckFeeRate, cannot be bypassed using m_package_feerates because,
|
||||
// while a tx could be package CPFP'd when entering the mempool, we do not have a DoS-resistant
|
||||
// method of ensuring the tx remains bumped. For example, the fee-bumping child could disappear
|
||||
// due to a replacement.
|
||||
if (!bypass_limits && ws.m_modified_fees < m_pool.m_min_relay_feerate.GetFee(ws.m_vsize)) {
|
||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met",
|
||||
strprintf("%d < %d", ws.m_modified_fees, m_pool.m_min_relay_feerate.GetFee(ws.m_vsize)));
|
||||
}
|
||||
// No individual transactions are allowed below the mempool min feerate except from disconnected
|
||||
// blocks and transactions in a package. Package transactions will be checked using package
|
||||
// feerate later.
|
||||
if (!bypass_limits && !args.m_package_feerates && !CheckFeeRate(ws.m_vsize, ws.m_modified_fees, state)) return false;
|
||||
|
||||
ws.m_iters_conflicting = m_pool.GetIterSet(ws.m_conflicts);
|
||||
|
Loading…
x
Reference in New Issue
Block a user