Skip to content

Commit 8247f07

Browse files
committed
Restore -mempoolreplacement option to allow disabling opt-in RBF
This partially reverts commit 8053e5c.
1 parent a568b33 commit 8247f07

File tree

6 files changed

+40
-4
lines changed

6 files changed

+40
-4
lines changed

src/init.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ void SetupServerArgs(ArgsManager& argsman)
649649
MAX_OP_RETURN_RELAY),
650650
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
651651
argsman.AddArg("-mempoolfullrbf", strprintf("Accept transaction replace-by-fee without requiring replaceability signaling (default: %u)", DEFAULT_MEMPOOL_FULL_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
652+
argsman.AddArg("-mempoolreplacement", strprintf("Enable transaction replacement in the memory pool (default: %u)", DEFAULT_ENABLE_REPLACEMENT), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
652653
argsman.AddArg("-permitbaremultisig", strprintf("Relay transactions creating non-P2SH multisig outputs (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY,
653654
OptionsCategory::NODE_RELAY);
654655
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
@@ -1058,6 +1059,14 @@ bool AppInitParameterInteraction(const ArgsManager& args)
10581059
}
10591060
}
10601061

1062+
gEnableReplacement = args.GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT);
1063+
if ((!gEnableReplacement) && args.IsArgSet("-mempoolreplacement")) {
1064+
// Minimal effort at forwards compatibility
1065+
std::string replacement_opt = args.GetArg("-mempoolreplacement", ""); // default is impossible
1066+
std::vector<std::string> replacement_modes = util::SplitString(replacement_opt, ",");
1067+
gEnableReplacement = (std::find(replacement_modes.begin(), replacement_modes.end(), "fee") != replacement_modes.end());
1068+
}
1069+
10611070
// Also report errors from parsing before daemonization
10621071
{
10631072
kernel::Notifications notifications{};

src/policy/policy.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ static constexpr unsigned int MAX_STANDARD_TX_SIGOPS_COST{MAX_BLOCK_SIGOPS_COST/
3636
static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE{1000};
3737
/** Default for -bytespersigop */
3838
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20};
39+
/** Default for -mempoolreplacement */
40+
static constexpr bool DEFAULT_ENABLE_REPLACEMENT{true};
3941
/** Default for -permitbaremultisig */
4042
static constexpr bool DEFAULT_PERMIT_BAREMULTISIG{true};
4143
/** The maximum number of witness stack items in a standard P2WSH script */

src/policy/settings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77

88
#include <policy/policy.h>
99

10+
bool gEnableReplacement = DEFAULT_ENABLE_REPLACEMENT;
1011
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;

src/policy/settings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#ifndef BITCOIN_POLICY_SETTINGS_H
77
#define BITCOIN_POLICY_SETTINGS_H
88

9+
extern bool gEnableReplacement;
910
extern unsigned int nBytesPerSigOp;
1011

1112
#endif // BITCOIN_POLICY_SETTINGS_H

src/validation.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,14 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
858858
//
859859
// Replaceability signaling of the original transactions may be
860860
// ignored due to node setting.
861-
const bool allow_rbf{(m_pool.m_opts.full_rbf || ignore_rejects.count("txn-mempool-conflict")) || SignalsOptInRBF(*ptxConflicting) || ptxConflicting->version == TRUC_VERSION};
861+
bool allow_rbf;
862+
if (m_pool.m_opts.full_rbf || ignore_rejects.count("txn-mempool-conflict")) {
863+
allow_rbf = true;
864+
} else if (!gEnableReplacement) {
865+
allow_rbf = false;
866+
} else {
867+
allow_rbf = SignalsOptInRBF(*ptxConflicting) || ptxConflicting->version == TRUC_VERSION;
868+
}
862869
if (!allow_rbf) {
863870
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
864871
}

test/functional/feature_rbf.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def add_options(self, parser):
2525
self.add_wallet_options(parser)
2626

2727
def set_test_params(self):
28-
self.num_nodes = 2
28+
self.num_nodes = 3
2929
# both nodes disable full-rbf to test BIP125 signaling
3030
self.extra_args = [
3131
[
@@ -39,6 +39,10 @@ def set_test_params(self):
3939
[
4040
"-mempoolfullrbf=0",
4141
],
42+
[
43+
"-acceptnonstdtxn=1",
44+
"-mempoolreplacement=0",
45+
],
4246
]
4347
self.supports_cli = False
4448

@@ -114,17 +118,24 @@ def test_simple_doublespend(self):
114118
# we use MiniWallet to create a transaction template with inputs correctly set,
115119
# and modify the output (amount, scriptPubKey) according to our needs
116120
tx = self.wallet.create_self_transfer()["tx"]
117-
tx1a_txid = self.nodes[0].sendrawtransaction(tx.serialize().hex())
121+
tx1a_hex = tx.serialize().hex()
122+
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex)
123+
assert_equal(tx1a_txid, self.nodes[2].sendrawtransaction(tx1a_hex))
118124

119125
# Should fail because we haven't changed the fee
120126
tx.vout[0].scriptPubKey[-1] ^= 1
121127

122128
# This will raise an exception due to insufficient fee
123-
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex(), 0)
129+
tx1b_hex = tx.serialize().hex()
130+
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
131+
# This will raise an exception due to transaction replacement being disabled
132+
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[2].sendrawtransaction, tx1b_hex, 0)
124133

125134
# Extra 0.1 BTC fee
126135
tx.vout[0].nValue -= int(0.1 * COIN)
127136
tx1b_hex = tx.serialize().hex()
137+
# Replacement still disabled even with "enough fee"
138+
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[2].sendrawtransaction, tx1b_hex, 0)
128139
# Works when enabled
129140
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
130141

@@ -135,6 +146,11 @@ def test_simple_doublespend(self):
135146

136147
assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid))
137148

149+
# Third node is running mempoolreplacement=0, will not replace originally-seen txn
150+
mempool = self.nodes[2].getrawmempool()
151+
assert tx1a_txid in mempool
152+
assert tx1b_txid not in mempool
153+
138154
def test_doublespend_chain(self):
139155
"""Doublespend of a long chain"""
140156

0 commit comments

Comments
 (0)