mempool_args.cpp raw
1 // Copyright (c) 2022 The Limenka developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <node/mempool_args.h>
6
7 #include <kernel/mempool_limits.h>
8 #include <kernel/mempool_options.h>
9
10 #include <common/args.h>
11 #include <common/messages.h>
12 #include <common/settings.h>
13 #include <consensus/amount.h>
14 #include <kernel/chainparams.h>
15 #include <logging.h>
16 #include <node/interface_ui.h>
17 #include <policy/feerate.h>
18 #include <policy/fees.h>
19 #include <policy/policy.h>
20 #include <tinyformat.h>
21 #include <univalue.h>
22 #include <util/moneystr.h>
23 #include <util/result.h>
24 #include <util/strencodings.h>
25 #include <util/string.h>
26 #include <util/translation.h>
27
28 #include <chrono>
29 #include <cstdint>
30 #include <memory>
31 #include <string_view>
32 #include <utility>
33
34 using common::AmountErrMsg;
35 using kernel::MemPoolLimits;
36 using kernel::MemPoolOptions;
37
38 //! Maximum mempool size on 32-bit systems.
39 static constexpr int MAX_32BIT_MEMPOOL_MB{500};
40
41 namespace {
42 void ApplyArgsManOptions(const ArgsManager& argsman, MemPoolLimits& mempool_limits)
43 {
44 mempool_limits.ancestor_count = argsman.GetIntArg("-limitancestorcount", mempool_limits.ancestor_count);
45
46 if (auto vkb = argsman.GetIntArg("-limitancestorsize")) mempool_limits.ancestor_size_vbytes = *vkb * 1'000;
47
48 mempool_limits.descendant_count = argsman.GetIntArg("-limitdescendantcount", mempool_limits.descendant_count);
49
50 if (auto vkb = argsman.GetIntArg("-limitdescendantsize")) mempool_limits.descendant_size_vbytes = *vkb * 1'000;
51 }
52 }
53
54 util::Result<std::pair<int32_t, int>> ParseDustDynamicOpt(std::string_view optstr, const unsigned int max_fee_estimate_blocks)
55 {
56 if (optstr == "0" || optstr == "off") {
57 return std::pair<int32_t, int>(0, DEFAULT_DUST_RELAY_MULTIPLIER);
58 }
59
60 int multiplier{DEFAULT_DUST_RELAY_MULTIPLIER};
61 if (auto pos = optstr.find('*'); pos != optstr.npos) {
62 int64_t parsed;
63 if ((!ParseFixedPoint(optstr.substr(0, pos), 3, &parsed)) || parsed > std::numeric_limits<int>::max() || parsed < 1) {
64 return util::Error{_("failed to parse multiplier")};
65 }
66 multiplier = parsed;
67 optstr.remove_prefix(pos + 1);
68 }
69
70 if (optstr.rfind("target:", 0) == 0) {
71 const auto val = ToIntegral<uint16_t>(optstr.substr(7));
72 if (!val) {
73 return util::Error{_("failed to parse target block count")};
74 }
75 if (*val < 2) {
76 return util::Error{_("target must be at least 2 blocks")};
77 }
78 if (Assume(max_fee_estimate_blocks) && *val > max_fee_estimate_blocks) {
79 return util::Error{strprintf(_("target can only be at most %s blocks"), max_fee_estimate_blocks)};
80 }
81 return std::pair<int32_t, int>(-*val, multiplier);
82 } else if (optstr.rfind("mempool:", 0) == 0) {
83 const auto val = ToIntegral<int32_t>(optstr.substr(8));
84 if (!val) {
85 return util::Error{_("failed to parse mempool position")};
86 }
87 if (*val < 1) {
88 return util::Error{_("mempool position must be at least 1 kB")};
89 }
90 return std::pair<int32_t, int>(*val, multiplier);
91 } else {
92 return util::Error{strprintf(_("\"%s\""), optstr)};
93 }
94 }
95
96 void ApplyPermitEphemeralOption(const common::SettingsValue& value, MemPoolOptions& mempool_opts)
97 {
98 std::optional<bool> flag_anchor, flag_send, flag_dust;
99 if (SettingToBool(value, false)) {
100 flag_anchor = flag_send = flag_dust = true;
101 }
102 for (auto& opt : util::SplitString(SettingToString(value).value_or(""), ",+")) {
103 bool v{true};
104 if (opt.size() && opt[0] == '-') {
105 opt.erase(opt.begin());
106 v = false;
107 }
108 if (opt == "anchor") {
109 flag_anchor = v;
110 } else if (opt == "dust") {
111 flag_dust = v;
112 } else if (opt == "send") {
113 flag_send = v;
114 } else if (opt == "reject" || opt == "0") {
115 flag_anchor = flag_send = flag_dust = !v;
116 }
117 }
118
119 if (!flag_send) {
120 flag_send = flag_dust.value_or(false) && !flag_anchor.value_or(false);
121 }
122 if (!flag_dust) {
123 flag_dust = flag_send;
124 }
125 if (!flag_anchor) {
126 flag_anchor = true;
127 }
128
129 mempool_opts.permitephemeral_dust = *flag_dust;
130 mempool_opts.permitephemeral_anchor = *flag_anchor;
131 mempool_opts.permitephemeral_send = *flag_send;
132 }
133
134 util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainParams& chainparams, MemPoolOptions& mempool_opts)
135 {
136 mempool_opts.check_ratio = argsman.GetIntArg("-checkmempool", mempool_opts.check_ratio);
137
138 if (auto mb = argsman.GetIntArg("-maxmempool")) {
139 constexpr bool is_32bit{sizeof(void*) == 4};
140 if (is_32bit && *mb > MAX_32BIT_MEMPOOL_MB) {
141 return util::Error{Untranslated(strprintf("-maxmempool is set to %i but can't be over %i MB on 32-bit systems", *mb, MAX_32BIT_MEMPOOL_MB))};
142 }
143 mempool_opts.max_size_bytes = *mb * 1'000'000;
144 }
145
146 if (auto hours = argsman.GetIntArg("-mempoolexpiry")) mempool_opts.expiry = std::chrono::hours{*hours};
147
148 mempool_opts.minrelaycoinblocks = argsman.GetIntArg("-minrelaycoinblocks", mempool_opts.minrelaycoinblocks);
149 mempool_opts.minrelaymaturity = argsman.GetIntArg("-minrelaymaturity", mempool_opts.minrelaymaturity);
150
151 // incremental relay fee sets the minimum feerate increase necessary for replacement in the mempool
152 // and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting.
153 if (argsman.IsArgSet("-incrementalrelayfee")) {
154 if (std::optional<CAmount> inc_relay_fee = ParseMoney(argsman.GetArg("-incrementalrelayfee", ""))) {
155 mempool_opts.incremental_relay_feerate = CFeeRate{inc_relay_fee.value()};
156 } else {
157 return util::Error{AmountErrMsg("incrementalrelayfee", argsman.GetArg("-incrementalrelayfee", ""))};
158 }
159 }
160
161 static_assert(DEFAULT_MIN_RELAY_TX_FEE == DEFAULT_INCREMENTAL_RELAY_FEE);
162 if (argsman.IsArgSet("-minrelaytxfee")) {
163 if (std::optional<CAmount> min_relay_feerate = ParseMoney(argsman.GetArg("-minrelaytxfee", ""))) {
164 // High fee check is done afterward in CWallet::Create()
165 mempool_opts.min_relay_feerate = CFeeRate{min_relay_feerate.value()};
166 } else {
167 return util::Error{AmountErrMsg("minrelaytxfee", argsman.GetArg("-minrelaytxfee", ""))};
168 }
169 } else if (mempool_opts.incremental_relay_feerate > mempool_opts.min_relay_feerate) {
170 // Allow only setting incremental fee to control both
171 mempool_opts.min_relay_feerate = mempool_opts.incremental_relay_feerate;
172 LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n", mempool_opts.min_relay_feerate.ToString());
173 }
174
175 // Feerate used to define dust. Shouldn't be changed lightly as old
176 // implementations may inadvertently create non-standard transactions
177 if (argsman.IsArgSet("-dustrelayfee")) {
178 if (std::optional<CAmount> parsed = ParseMoney(argsman.GetArg("-dustrelayfee", ""))) {
179 mempool_opts.dust_relay_feerate = CFeeRate{parsed.value()};
180 } else {
181 return util::Error{AmountErrMsg("dustrelayfee", argsman.GetArg("-dustrelayfee", ""))};
182 }
183 }
184 if (argsman.IsArgSet("-dustdynamic")) {
185 const auto optstr = argsman.GetArg("-dustdynamic", DEFAULT_DUST_DYNAMIC);
186 // TODO: Should probably reject target-based dustdynamic if there's no estimator, but currently we're checking parameters long before we have the fee estimator initialised
187 const auto max_fee_estimate_blocks = mempool_opts.estimator ? mempool_opts.estimator->HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE) : (CBlockPolicyEstimator::LONG_BLOCK_PERIODS * CBlockPolicyEstimator::LONG_SCALE);
188 const auto parsed = ParseDustDynamicOpt(optstr, max_fee_estimate_blocks);
189 if (!parsed) {
190 return util::Error{strprintf(_("Invalid mode for dustdynamic: %s"), util::ErrorString(parsed))};
191 }
192 mempool_opts.dust_relay_target = parsed->first;
193 mempool_opts.dust_relay_multiplier = parsed->second;
194 }
195
196 mempool_opts.maxtxlegacysigops = argsman.GetIntArg("-maxtxlegacysigops", mempool_opts.maxtxlegacysigops);
197
198 mempool_opts.permitbareanchor = argsman.GetBoolArg("-permitbareanchor", mempool_opts.permitbareanchor);
199
200 mempool_opts.permit_bare_pubkey = argsman.GetBoolArg("-permitbarepubkey", DEFAULT_PERMIT_BAREPUBKEY);
201
202 mempool_opts.permit_bare_multisig = argsman.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
203
204 mempool_opts.reject_parasites = argsman.GetBoolArg("-rejectparasites", DEFAULT_REJECT_PARASITES);
205
206 mempool_opts.reject_tokens = argsman.GetBoolArg("-rejecttokens", DEFAULT_REJECT_TOKENS);
207
208 mempool_opts.subdustfeepenalty = argsman.GetBoolArg("-subdustfeepenalty", DEFAULT_SUBDUSTFEEPENALTY);
209
210 if (argsman.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER)) {
211 mempool_opts.max_datacarrier_bytes = argsman.GetIntArg("-datacarriersize", MAX_OP_RETURN_RELAY);
212 if (mempool_opts.max_datacarrier_bytes.value() > MAX_OUTPUT_DATA_SIZE) {
213 LogWarning("Limiting datacarriersize to %d", MAX_OUTPUT_DATA_SIZE);
214 mempool_opts.max_datacarrier_bytes = MAX_OUTPUT_DATA_SIZE;
215 }
216 } else {
217 mempool_opts.max_datacarrier_bytes = std::nullopt;
218 }
219 mempool_opts.datacarrier_fullcount = argsman.GetBoolArg("-datacarrierfullcount", DEFAULT_DATACARRIER_FULLCOUNT);
220 mempool_opts.accept_non_std_datacarrier = argsman.GetBoolArg("-acceptnonstddatacarrier", DEFAULT_ACCEPT_NON_STD_DATACARRIER);
221
222 mempool_opts.permitbaredatacarrier = argsman.GetBoolArg("-permitbaredatacarrier", mempool_opts.permitbaredatacarrier);
223
224 mempool_opts.max_op_return_outputs = argsman.GetIntArg("-maxopreturnrelay", DEFAULT_MAX_OP_RETURN_OUTPUTS);
225 mempool_opts.taproot_witness_limit = argsman.GetIntArg("-taprootwitnesslimit", DEFAULT_TAPROOT_WITNESS_LIMIT);
226 mempool_opts.reject_taproot = argsman.GetBoolArg("-rejecttaproot", mempool_opts.reject_taproot);
227
228 mempool_opts.require_standard = !argsman.GetBoolArg("-acceptnonstdtxn", DEFAULT_ACCEPT_NON_STD_TXN);
229
230 mempool_opts.acceptunknownwitness = argsman.GetBoolArg("-acceptunknownwitness", mempool_opts.acceptunknownwitness);
231
232 if (argsman.IsArgSet("-mempoolreplacement") || argsman.IsArgSet("-mempoolfullrbf")) {
233 // Generally, mempoolreplacement overrides mempoolfullrbf, but the latter is used to infer intent in some cases
234 std::optional<bool> optin_flag;
235 bool fee_flag{false};
236 if (argsman.GetBoolArg("-mempoolreplacement", false)) {
237 fee_flag = true;
238 } else {
239 for (auto& opt : util::SplitString(argsman.GetArg("-mempoolreplacement", ""), ",+")) {
240 if (opt == "optin") {
241 optin_flag = true;
242 } else if (opt == "-optin") {
243 optin_flag = false;
244 } else if (opt == "fee") {
245 fee_flag = true;
246 }
247 }
248 }
249 if (optin_flag.value_or(false)) {
250 // "optin" is explicitly specified
251 mempool_opts.rbf_policy = RBFPolicy::OptIn;
252 } else if (argsman.GetBoolArg("-mempoolfullrbf", false)) {
253 const bool mempoolreplacement_false{argsman.IsArgSet("-mempoolreplacement") && !(fee_flag || optin_flag.has_value())};
254 if (mempoolreplacement_false) {
255 // This is a contradiction, but override rather than error
256 InitWarning(_("False mempoolreplacement option contradicts true mempoolfullrbf; disallowing all RBF"));
257 mempool_opts.rbf_policy = RBFPolicy::Never;
258 } else {
259 mempool_opts.rbf_policy = RBFPolicy::Always;
260 }
261 } else if (!optin_flag.value_or(true)) {
262 // "-optin" is explicitly specified
263 mempool_opts.rbf_policy = fee_flag ? RBFPolicy::Always : RBFPolicy::Never;
264 } else if (fee_flag) {
265 // Just "fee" by itself
266 if (!argsman.GetBoolArg("-mempoolfullrbf", true)) {
267 mempool_opts.rbf_policy = RBFPolicy::OptIn;
268 } else {
269 // Fallback to default, unless it's been changed to Never
270 if (mempool_opts.rbf_policy == RBFPolicy::Never) {
271 mempool_opts.rbf_policy = RBFPolicy::Always;
272 }
273 }
274 } else if (!argsman.IsArgSet("-mempoolreplacement")) {
275 // mempoolfullrbf is always explicitly false here
276 // Fallback to default, as long as it isn't Always
277 if (mempool_opts.rbf_policy == RBFPolicy::Always) {
278 mempool_opts.rbf_policy = RBFPolicy::OptIn;
279 }
280 } else {
281 // mempoolreplacement is explicitly false here
282 mempool_opts.rbf_policy = RBFPolicy::Never;
283 }
284 }
285
286 if (argsman.IsArgSet("-mempooltruc")) {
287 std::optional<bool> accept_flag, enforce_flag;
288 if (argsman.GetBoolArg("-mempooltruc", false)) {
289 enforce_flag = true;
290 }
291 for (auto& opt : util::SplitString(argsman.GetArg("-mempooltruc", ""), ",+")) {
292 if (opt == "optin" || opt == "enforce") {
293 enforce_flag = true;
294 } else if (opt == "-optin" || opt == "-enforce") {
295 enforce_flag = false;
296 } else if (opt == "accept") {
297 accept_flag = true;
298 } else if (opt == "reject" || opt == "0") {
299 accept_flag = false;
300 }
301 }
302
303 if (accept_flag && !*accept_flag) { // reject
304 mempool_opts.truc_policy = TRUCPolicy::Reject;
305 } else if (enforce_flag && *enforce_flag) { // enforce
306 mempool_opts.truc_policy = TRUCPolicy::Enforce;
307 } else if ((!accept_flag) && !enforce_flag) {
308 // nothing specified, leave at default
309 } else { // accept or -enforce
310 mempool_opts.truc_policy = TRUCPolicy::Accept;
311 }
312 }
313
314 if (argsman.IsArgSet("-permitephemeral")) {
315 ApplyPermitEphemeralOption(argsman.GetSetting("-permitephemeral"), mempool_opts);
316 }
317
318 mempool_opts.persist_v1_dat = argsman.GetBoolArg("-persistmempoolv1", mempool_opts.persist_v1_dat);
319
320 ApplyArgsManOptions(argsman, mempool_opts.limits);
321
322 return {};
323 }
324
325 void ApplyMempoolFlagDay(int64_t tip_mtp, MemPoolOptions& mempool_opts, const ArgsManager& argsman)
326 {
327 // If user explicitly set -rejecttaproot, defer to their choice.
328 if (argsman.IsArgSet("-rejecttaproot")) {
329 return;
330 }
331 // After flag day, default to rejecting taproot transactions in mempool.
332 if (tip_mtp >= TAPROOT_REJECT_MTP) {
333 mempool_opts.reject_taproot = true;
334 }
335 }
336