@@ -78,6 +78,56 @@ def fill_mempool(self):
78
78
assert_equal (node .getmempoolinfo ()['minrelaytxfee' ], Decimal ('0.00001000' ))
79
79
assert_greater_than (node .getmempoolinfo ()['mempoolminfee' ], Decimal ('0.00001000' ))
80
80
81
+ def test_rbf_carveout_disallowed (self ):
82
+ node = self .nodes [0 ]
83
+
84
+ self .log .info ("Check that individually-evaluated transactions in a package don't increase package limits for other subpackage parts" )
85
+
86
+ # We set chain limits to 2 ancestors, 1 descendant, then try to get a parents-and-child chain of 2 in mempool
87
+ #
88
+ # A: Solo transaction to be RBF'd (to bump descendant limit for package later)
89
+ # B: First transaction in package, RBFs A by itself under individual evaluation, which would give it +1 descendant limit
90
+ # C: Second transaction in package, spends B. If the +1 descendant limit persisted, would make it into mempool
91
+
92
+ self .restart_node (0 , extra_args = self .extra_args [0 ] + ["-limitancestorcount=2" , "-limitdescendantcount=1" ])
93
+
94
+ # Generate a confirmed utxo we will double-spend
95
+ rbf_utxo = self .wallet .send_self_transfer (
96
+ from_node = node ,
97
+ confirmed_only = True
98
+ )["new_utxo" ]
99
+ self .generate (node , 1 )
100
+
101
+ # tx_A needs to be RBF'd, set minfee at set size
102
+ A_weight = 1000
103
+ mempoolmin_feerate = node .getmempoolinfo ()["mempoolminfee" ]
104
+ tx_A = self .wallet .send_self_transfer (
105
+ from_node = node ,
106
+ fee = (mempoolmin_feerate / 1000 ) * (A_weight // 4 ) + Decimal ('0.000001' ),
107
+ target_weight = A_weight ,
108
+ utxo_to_spend = rbf_utxo ,
109
+ confirmed_only = True
110
+ )
111
+
112
+ # RBF's tx_A, is not yet submitted
113
+ tx_B = self .wallet .create_self_transfer (
114
+ fee = tx_A ["fee" ] * 4 ,
115
+ target_weight = A_weight ,
116
+ utxo_to_spend = rbf_utxo ,
117
+ confirmed_only = True
118
+ )
119
+
120
+ # Spends tx_B's output, too big for cpfp carveout (because that would also increase the descendant limit by 1)
121
+ non_cpfp_carveout_weight = 40001 # EXTRA_DESCENDANT_TX_SIZE_LIMIT + 1
122
+ tx_C = self .wallet .create_self_transfer (
123
+ target_weight = non_cpfp_carveout_weight ,
124
+ fee = (mempoolmin_feerate / 1000 ) * (non_cpfp_carveout_weight // 4 ) + Decimal ('0.000001' ),
125
+ utxo_to_spend = tx_B ["new_utxo" ],
126
+ confirmed_only = True
127
+ )
128
+
129
+ assert_raises_rpc_error (- 26 , "too-long-mempool-chain" , node .submitpackage , [tx_B ["hex" ], tx_C ["hex" ]])
130
+
81
131
def test_mid_package_eviction (self ):
82
132
node = self .nodes [0 ]
83
133
self .log .info ("Check a package where each parent passes the current mempoolminfee but would cause eviction before package submission terminates" )
@@ -324,6 +374,7 @@ def run_test(self):
324
374
325
375
self .test_mid_package_replacement ()
326
376
self .test_mid_package_eviction ()
377
+ self .test_rbf_carveout_disallowed ()
327
378
328
379
329
380
if __name__ == '__main__' :
0 commit comments