Skip to content

Commit 111a23d

Browse files
committed
Remove -mempoolfullrbf option
1 parent fc642c3 commit 111a23d

File tree

8 files changed

+8
-230
lines changed

8 files changed

+8
-230
lines changed

src/init.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,6 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
633633
"is of this size or less (default: %u)",
634634
MAX_OP_RETURN_RELAY),
635635
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
636-
argsman.AddArg("-mempoolfullrbf", strprintf("(DEPRECATED) Accept transaction replace-by-fee without requiring replaceability signaling (default: %u)", DEFAULT_MEMPOOL_FULL_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
637636
argsman.AddArg("-permitbaremultisig", strprintf("Relay transactions creating non-P2SH multisig outputs (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY,
638637
OptionsCategory::NODE_RELAY);
639638
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",

src/kernel/mempool_options.h

-3
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ static constexpr unsigned int DEFAULT_MAX_MEMPOOL_SIZE_MB{300};
2121
static constexpr unsigned int DEFAULT_BLOCKSONLY_MAX_MEMPOOL_SIZE_MB{5};
2222
/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */
2323
static constexpr unsigned int DEFAULT_MEMPOOL_EXPIRY_HOURS{336};
24-
/** Default for -mempoolfullrbf, if the transaction replaceability signaling is ignored */
25-
static constexpr bool DEFAULT_MEMPOOL_FULL_RBF{true};
2624
/** Whether to fall back to legacy V1 serialization when writing mempool.dat */
2725
static constexpr bool DEFAULT_PERSIST_V1_DAT{false};
2826
/** Default for -acceptnonstdtxn */
@@ -55,7 +53,6 @@ struct MemPoolOptions {
5553
std::optional<unsigned> max_datacarrier_bytes{DEFAULT_ACCEPT_DATACARRIER ? std::optional{MAX_OP_RETURN_RELAY} : std::nullopt};
5654
bool permit_bare_multisig{DEFAULT_PERMIT_BAREMULTISIG};
5755
bool require_standard{true};
58-
bool full_rbf{DEFAULT_MEMPOOL_FULL_RBF};
5956
bool persist_v1_dat{DEFAULT_PERSIST_V1_DAT};
6057
MemPoolLimits limits{};
6158

src/node/mempool_args.cpp

-5
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,6 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
9292
return util::Error{strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.GetChainTypeString())};
9393
}
9494

95-
mempool_opts.full_rbf = argsman.GetBoolArg("-mempoolfullrbf", mempool_opts.full_rbf);
96-
if (!mempool_opts.full_rbf) {
97-
LogInfo("Warning: mempoolfullrbf=0 set but deprecated and will be removed in a future release\n");
98-
}
99-
10095
mempool_opts.persist_v1_dat = argsman.GetBoolArg("-persistmempoolv1", mempool_opts.persist_v1_dat);
10196

10297
ApplyArgsManOptions(argsman, mempool_opts.limits);

src/rpc/mempool.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
679679
ret.pushKV("minrelaytxfee", ValueFromAmount(pool.m_opts.min_relay_feerate.GetFeePerK()));
680680
ret.pushKV("incrementalrelayfee", ValueFromAmount(pool.m_opts.incremental_relay_feerate.GetFeePerK()));
681681
ret.pushKV("unbroadcastcount", uint64_t{pool.GetUnbroadcastTxs().size()});
682-
ret.pushKV("fullrbf", pool.m_opts.full_rbf);
682+
ret.pushKV("fullrbf", true);
683683
return ret;
684684
}
685685

src/validation.cpp

+2-22
Original file line numberDiff line numberDiff line change
@@ -820,30 +820,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
820820
const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
821821
if (ptxConflicting) {
822822
if (!args.m_allow_replacement) {
823-
// Transaction conflicts with a mempool tx, but we're not allowing replacements.
823+
// Transaction conflicts with a mempool tx, but we're not allowing replacements in this context.
824824
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
825825
}
826-
if (!ws.m_conflicts.count(ptxConflicting->GetHash()))
827-
{
828-
// Transactions that don't explicitly signal replaceability are
829-
// *not* replaceable with the current logic, even if one of their
830-
// unconfirmed ancestors signals replaceability. This diverges
831-
// from BIP125's inherited signaling description (see CVE-2021-31876).
832-
// Applications relying on first-seen mempool behavior should
833-
// check all unconfirmed ancestors; otherwise an opt-in ancestor
834-
// might be replaced, causing removal of this descendant.
835-
//
836-
// All TRUC transactions are considered replaceable.
837-
//
838-
// Replaceability signaling of the original transactions may be
839-
// ignored due to node setting.
840-
const bool allow_rbf{m_pool.m_opts.full_rbf || SignalsOptInRBF(*ptxConflicting) || ptxConflicting->version == TRUC_VERSION};
841-
if (!allow_rbf) {
842-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
843-
}
844-
845-
ws.m_conflicts.insert(ptxConflicting->GetHash());
846-
}
826+
ws.m_conflicts.insert(ptxConflicting->GetHash());
847827
}
848828
}
849829

test/functional/feature_rbf.py

+5-156
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from test_framework.messages import (
1010
MAX_BIP125_RBF_SEQUENCE,
1111
COIN,
12-
SEQUENCE_FINAL,
1312
)
1413
from test_framework.test_framework import BitcoinTestFramework
1514
from test_framework.util import (
@@ -26,18 +25,15 @@ def add_options(self, parser):
2625

2726
def set_test_params(self):
2827
self.num_nodes = 2
29-
# both nodes disable full-rbf to test BIP125 signaling
3028
self.extra_args = [
3129
[
32-
"-mempoolfullrbf=0",
3330
"-limitancestorcount=50",
3431
"-limitancestorsize=101",
3532
"-limitdescendantcount=200",
3633
"-limitdescendantsize=101",
3734
],
38-
# second node has default mempool parameters, besides mempoolfullrbf being disabled
35+
# second node has default mempool parameters
3936
[
40-
"-mempoolfullrbf=0",
4137
],
4238
]
4339
self.supports_cli = False
@@ -69,18 +65,12 @@ def run_test(self):
6965
self.log.info("Running test too many replacements using default mempool params...")
7066
self.test_too_many_replacements_with_default_mempool_params()
7167

72-
self.log.info("Running test opt-in...")
73-
self.test_opt_in()
74-
7568
self.log.info("Running test RPC...")
7669
self.test_rpc()
7770

7871
self.log.info("Running test prioritised transactions...")
7972
self.test_prioritised_transactions()
8073

81-
self.log.info("Running test no inherited signaling...")
82-
self.test_no_inherited_signaling()
83-
8474
self.log.info("Running test replacement relay fee...")
8575
self.test_replacement_relay_fee()
8676

@@ -426,14 +416,12 @@ def test_too_many_replacements_with_default_mempool_params(self):
426416
for graph_num in range(num_tx_graphs):
427417
root_utxos.append(wallet.get_utxo())
428418

429-
optin_parent_tx = wallet.send_self_transfer_multi(
419+
parent_tx = wallet.send_self_transfer_multi(
430420
from_node=normal_node,
431-
sequence=MAX_BIP125_RBF_SEQUENCE,
432421
utxos_to_spend=[root_utxos[graph_num]],
433422
num_outputs=txs_per_graph,
434423
)
435-
assert_equal(True, normal_node.getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable'])
436-
new_utxos = optin_parent_tx['new_utxos']
424+
new_utxos = parent_tx['new_utxos']
437425

438426
for utxo in new_utxos:
439427
# Create spends for each output from the "root" of this graph.
@@ -470,81 +458,6 @@ def test_too_many_replacements_with_default_mempool_params(self):
470458
self.generate(normal_node, 1)
471459
self.wallet.rescan_utxos()
472460

473-
def test_opt_in(self):
474-
"""Replacing should only work if orig tx opted in"""
475-
tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
476-
477-
# Create a non-opting in transaction
478-
tx1a_utxo = self.wallet.send_self_transfer(
479-
from_node=self.nodes[0],
480-
utxo_to_spend=tx0_outpoint,
481-
sequence=SEQUENCE_FINAL,
482-
fee=Decimal("0.1"),
483-
)["new_utxo"]
484-
485-
# This transaction isn't shown as replaceable
486-
assert_equal(self.nodes[0].getmempoolentry(tx1a_utxo["txid"])['bip125-replaceable'], False)
487-
488-
# Shouldn't be able to double-spend
489-
tx1b_hex = self.wallet.create_self_transfer(
490-
utxo_to_spend=tx0_outpoint,
491-
sequence=0,
492-
fee=Decimal("0.2"),
493-
)["hex"]
494-
495-
# This will raise an exception
496-
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
497-
498-
tx1_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
499-
500-
# Create a different non-opting in transaction
501-
tx2a_utxo = self.wallet.send_self_transfer(
502-
from_node=self.nodes[0],
503-
utxo_to_spend=tx1_outpoint,
504-
sequence=0xfffffffe,
505-
fee=Decimal("0.1"),
506-
)["new_utxo"]
507-
508-
# Still shouldn't be able to double-spend
509-
tx2b_hex = self.wallet.create_self_transfer(
510-
utxo_to_spend=tx1_outpoint,
511-
sequence=0,
512-
fee=Decimal("0.2"),
513-
)["hex"]
514-
515-
# This will raise an exception
516-
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, 0)
517-
518-
# Now create a new transaction that spends from tx1a and tx2a
519-
# opt-in on one of the inputs
520-
# Transaction should be replaceable on either input
521-
522-
tx3a_txid = self.wallet.send_self_transfer_multi(
523-
from_node=self.nodes[0],
524-
utxos_to_spend=[tx1a_utxo, tx2a_utxo],
525-
sequence=[SEQUENCE_FINAL, 0xfffffffd],
526-
fee_per_output=int(0.1 * COIN),
527-
)["txid"]
528-
529-
# This transaction is shown as replaceable
530-
assert_equal(self.nodes[0].getmempoolentry(tx3a_txid)['bip125-replaceable'], True)
531-
532-
self.wallet.send_self_transfer(
533-
from_node=self.nodes[0],
534-
utxo_to_spend=tx1a_utxo,
535-
sequence=0,
536-
fee=Decimal("0.4"),
537-
)
538-
539-
# If tx3b was accepted, tx3c won't look like a replacement,
540-
# but make sure it is accepted anyway
541-
self.wallet.send_self_transfer(
542-
from_node=self.nodes[0],
543-
utxo_to_spend=tx2a_utxo,
544-
sequence=0,
545-
fee=Decimal("0.4"),
546-
)
547-
548461
def test_prioritised_transactions(self):
549462
# Ensure that fee deltas used via prioritisetransaction are
550463
# correctly used by replacement logic
@@ -629,69 +542,6 @@ def test_rpc(self):
629542
assert_equal(json0["vin"][0]["sequence"], 4294967293)
630543
assert_equal(json1["vin"][0]["sequence"], 4294967294)
631544

632-
def test_no_inherited_signaling(self):
633-
confirmed_utxo = self.wallet.get_utxo()
634-
635-
# Create an explicitly opt-in parent transaction
636-
optin_parent_tx = self.wallet.send_self_transfer(
637-
from_node=self.nodes[0],
638-
utxo_to_spend=confirmed_utxo,
639-
sequence=MAX_BIP125_RBF_SEQUENCE,
640-
fee_rate=Decimal('0.01'),
641-
)
642-
assert_equal(True, self.nodes[0].getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable'])
643-
644-
replacement_parent_tx = self.wallet.create_self_transfer(
645-
utxo_to_spend=confirmed_utxo,
646-
sequence=MAX_BIP125_RBF_SEQUENCE,
647-
fee_rate=Decimal('0.02'),
648-
)
649-
650-
# Test if parent tx can be replaced.
651-
res = self.nodes[0].testmempoolaccept(rawtxs=[replacement_parent_tx['hex']])[0]
652-
653-
# Parent can be replaced.
654-
assert_equal(res['allowed'], True)
655-
656-
# Create an opt-out child tx spending the opt-in parent
657-
parent_utxo = self.wallet.get_utxo(txid=optin_parent_tx['txid'])
658-
optout_child_tx = self.wallet.send_self_transfer(
659-
from_node=self.nodes[0],
660-
utxo_to_spend=parent_utxo,
661-
sequence=SEQUENCE_FINAL,
662-
fee_rate=Decimal('0.01'),
663-
)
664-
665-
# Reports true due to inheritance
666-
assert_equal(True, self.nodes[0].getmempoolentry(optout_child_tx['txid'])['bip125-replaceable'])
667-
668-
replacement_child_tx = self.wallet.create_self_transfer(
669-
utxo_to_spend=parent_utxo,
670-
sequence=SEQUENCE_FINAL,
671-
fee_rate=Decimal('0.02'),
672-
)
673-
674-
# Broadcast replacement child tx
675-
# BIP 125 :
676-
# 1. The original transactions signal replaceability explicitly or through inheritance as described in the above
677-
# Summary section.
678-
# The original transaction (`optout_child_tx`) doesn't signal RBF but its parent (`optin_parent_tx`) does.
679-
# The replacement transaction (`replacement_child_tx`) should be able to replace the original transaction.
680-
# See CVE-2021-31876 for further explanations.
681-
assert_equal(True, self.nodes[0].getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable'])
682-
assert_raises_rpc_error(-26, 'txn-mempool-conflict', self.nodes[0].sendrawtransaction, replacement_child_tx["hex"], 0)
683-
684-
self.log.info('Check that the child tx can still be replaced (via a tx that also replaces the parent)')
685-
replacement_parent_tx = self.wallet.send_self_transfer(
686-
from_node=self.nodes[0],
687-
utxo_to_spend=confirmed_utxo,
688-
sequence=SEQUENCE_FINAL,
689-
fee_rate=Decimal('0.03'),
690-
)
691-
# Check that child is removed and update wallet utxo state
692-
assert_raises_rpc_error(-5, 'Transaction not in mempool', self.nodes[0].getmempoolentry, optout_child_tx['txid'])
693-
self.wallet.get_utxo(txid=optout_child_tx['txid'])
694-
695545
def test_replacement_relay_fee(self):
696546
tx = self.wallet.send_self_transfer(from_node=self.nodes[0])['tx']
697547

@@ -702,12 +552,12 @@ def test_replacement_relay_fee(self):
702552
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex())
703553

704554
def test_fullrbf(self):
555+
# BIP125 signaling is not respected
705556

706557
confirmed_utxo = self.make_utxo(self.nodes[0], int(2 * COIN))
707-
self.restart_node(0, extra_args=["-mempoolfullrbf=1"])
708558
assert self.nodes[0].getmempoolinfo()["fullrbf"]
709559

710-
# Create an explicitly opt-out transaction
560+
# Create an explicitly opt-out BIP125 transaction, which will be ignored
711561
optout_tx = self.wallet.send_self_transfer(
712562
from_node=self.nodes[0],
713563
utxo_to_spend=confirmed_utxo,
@@ -718,7 +568,6 @@ def test_fullrbf(self):
718568

719569
conflicting_tx = self.wallet.create_self_transfer(
720570
utxo_to_spend=confirmed_utxo,
721-
sequence=SEQUENCE_FINAL,
722571
fee_rate=Decimal('0.02'),
723572
)
724573

test/functional/mempool_accept.py

-13
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ def set_test_params(self):
5555
self.num_nodes = 1
5656
self.extra_args = [[
5757
'-txindex','-permitbaremultisig=0',
58-
'-mempoolfullrbf=0',
5958
]] * self.num_nodes
6059
self.supports_cli = False
6160

@@ -155,25 +154,13 @@ def run_test(self):
155154
self.log.info('A transaction that replaces a mempool transaction')
156155
tx = tx_from_hex(raw_tx_0)
157156
tx.vout[0].nValue -= int(fee * COIN) # Double the fee
158-
tx.vin[0].nSequence = MAX_BIP125_RBF_SEQUENCE + 1 # Now, opt out of RBF
159157
raw_tx_0 = tx.serialize().hex()
160158
txid_0 = tx.rehash()
161159
self.check_mempool_result(
162160
result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': (2 * fee)}}],
163161
rawtxs=[raw_tx_0],
164162
)
165-
166-
self.log.info('A transaction that conflicts with an unconfirmed tx')
167-
# Send the transaction that replaces the mempool transaction and opts out of replaceability
168163
node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
169-
# take original raw_tx_0
170-
tx = tx_from_hex(raw_tx_0)
171-
tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee
172-
self.check_mempool_result(
173-
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'txn-mempool-conflict'}],
174-
rawtxs=[tx.serialize().hex()],
175-
maxfeerate=0,
176-
)
177164

178165
self.log.info('A transaction with missing inputs, that never existed')
179166
tx = tx_from_hex(raw_tx_0)

0 commit comments

Comments
 (0)