v31.1-roots.1 · Commit 2: [PATCH 02/17] feat(policy): preserve Roots parsing and local transaction checks
src/node/mempool_args.cpp
Before: src/node/mempool_args.cpp · After: src/node/mempool_args.cpp
File metadata
index d5ff50be96bf14377123c59b4c234e37b4298b7b..e017d1c27fb87ea46f6a1f9ab53d6dfb3f179bbf 100644
Download original patch · Release notes
Diff rows 1–255 of 255. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. The two line-number columns refer to before and after.
src/node/mempool_args.cpp
CHANGED
|
@@ -16,11 +16,19 @@
|
|
|
16
16
|
#include <policy/policy.h>
|
|
17
17
|
#include <tinyformat.h>
|
|
18
18
|
#include <txgraph.h>
|
|
19
|
+
#include <univalue.h>
|
|
19
20
|
#include <util/moneystr.h>
|
|
21
|
+
#include <util/strencodings.h>
|
|
22
|
+
#include <util/string.h>
|
|
20
23
|
#include <util/translation.h>
|
|
21
24
|
|
|
25
|
+
#include <algorithm>
|
|
22
26
|
#include <chrono>
|
|
27
|
+
#include <initializer_list>
|
|
28
|
+
#include <limits>
|
|
23
29
|
#include <memory>
|
|
30
|
+
#include <string_view>
|
|
31
|
+
#include <utility>
|
|
24
32
|
|
|
25
33
|
using common::AmountErrMsg;
|
|
26
34
|
using kernel::MemPoolLimits;
|
|
@@ -42,8 +50,181 @@ void ApplyArgsManOptions(const ArgsManager& argsman, MemPoolLimits& mempool_limi
|
|
|
42
50
|
}
|
|
43
51
|
}
|
|
44
52
|
|
|
53
|
+
util::Result<unsigned int> ParseDataCarrierCost(std::string_view value)
|
|
54
|
+
{
|
|
55
|
+
int64_t parsed;
|
|
56
|
+
constexpr int64_t max_cost{int64_t{std::numeric_limits<unsigned int>::max()} * 100 / WITNESS_SCALE_FACTOR};
|
|
57
|
+
if (!ParseFixedPoint(value, 2, &parsed) || parsed < 0 || parsed > max_cost) {
|
|
58
|
+
return util::Error{strprintf(_("Invalid -datacarriercost value: '%s' (expected a value from 0 to 1073741823.75 exactly representable in hundredths)"), value)};
|
|
59
|
+
}
|
|
60
|
+
return static_cast<unsigned int>(((parsed * WITNESS_SCALE_FACTOR) + 99) / 100);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
namespace {
|
|
64
|
+
// Check every token, including empty tokens that SplitString would discard.
|
|
65
|
+
bool ValidPolicyTokens(std::string_view value, std::initializer_list<std::string_view> allowed)
|
|
66
|
+
{
|
|
67
|
+
if (value.empty() || value == "0" || value == "1") return true;
|
|
68
|
+
while (true) {
|
|
69
|
+
const auto separator = value.find_first_of(",+");
|
|
70
|
+
const auto token = value.substr(0, separator);
|
|
71
|
+
if (std::find(allowed.begin(), allowed.end(), token) == allowed.end()) return false;
|
|
72
|
+
if (separator == value.npos) return true;
|
|
73
|
+
value.remove_prefix(separator + 1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
util::Result<void> ValidateRootsPolicyOptions(const ArgsManager& argsman)
|
|
79
|
+
{
|
|
80
|
+
// Check types before GetArg converts settings to strings. These are the
|
|
81
|
+
// Roots-added controls only; inherited Core getters remain permissive.
|
|
82
|
+
for (const char* option : {
|
|
83
|
+
"-subdustfeepenalty", "-rejectparasites", "-acceptunknownwitness",
|
|
84
|
+
"-datacarrierfullcount", "-permitbareanchor", "-rejecttokens",
|
|
85
|
+
"-permitbarepubkey", "-permitbaredatacarrier", "-acceptnonstddatacarrier",
|
|
86
|
+
"-maxscriptsize", "-bytespersigopstrict", "-maxtxlegacysigops",
|
|
87
|
+
"-minrelaycoinblocks", "-minrelaymaturity", "-blockmaxsize",
|
|
88
|
+
"-blockprioritysize", "-datacarriercost", "-dustdynamic",
|
|
89
|
+
"-mempoolreplacement", "-mempooltruc", "-permitephemeral", "-spkreuse"}) {
|
|
90
|
+
const auto value = argsman.GetSetting(option);
|
|
91
|
+
if (!value.isNull() && !value.isStr() && !value.isNum() && !value.isBool()) {
|
|
92
|
+
return util::Error{Untranslated(strprintf("Invalid %s value: expected a scalar policy value", option))};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const char* option : {
|
|
97
|
+
"-subdustfeepenalty", "-rejectparasites", "-acceptunknownwitness",
|
|
98
|
+
"-datacarrierfullcount", "-permitbareanchor", "-rejecttokens",
|
|
99
|
+
"-permitbarepubkey", "-permitbaredatacarrier", "-acceptnonstddatacarrier"}) {
|
|
100
|
+
if (const auto value = argsman.GetArg(option); value && !value->empty() && *value != "0" && *value != "1") {
|
|
101
|
+
return util::Error{Untranslated(strprintf("Invalid %s value: '%s' (expected empty, 0, or 1)", option, *value))};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const auto& [option, maximum] : {
|
|
106
|
+
std::pair<const char*, int64_t>{"-maxscriptsize", std::numeric_limits<unsigned int>::max()},
|
|
107
|
+
{"-bytespersigopstrict", std::numeric_limits<unsigned int>::max()},
|
|
108
|
+
{"-maxtxlegacysigops", std::numeric_limits<unsigned int>::max()},
|
|
109
|
+
{"-minrelaycoinblocks", std::numeric_limits<int64_t>::max()},
|
|
110
|
+
{"-minrelaymaturity", std::numeric_limits<int>::max()},
|
|
111
|
+
{"-blockmaxsize", MAX_BLOCK_SERIALIZED_SIZE},
|
|
112
|
+
{"-blockprioritysize", std::numeric_limits<int64_t>::max()}}) {
|
|
113
|
+
if (const auto value = argsman.GetArg(option)) {
|
|
114
|
+
std::string_view digits{*value};
|
|
115
|
+
// from_chars does not accept '+', but the existing integer syntax does.
|
|
116
|
+
if (digits.starts_with('+')) digits.remove_prefix(1);
|
|
117
|
+
const auto parsed = ToIntegral<int64_t>(digits);
|
|
118
|
+
if (!parsed || *parsed < 0 || *parsed > maximum || (value->starts_with('+') && digits.starts_with('-'))) {
|
|
119
|
+
return util::Error{Untranslated(strprintf("Invalid %s value: '%s' (expected an integer from 0 to %d)", option, *value, maximum))};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (const auto value = argsman.GetArg("-datacarriercost")) {
|
|
125
|
+
const auto parsed = ParseDataCarrierCost(*value);
|
|
126
|
+
if (!parsed) return util::Error{util::ErrorString(parsed)};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (const auto value = argsman.GetArg("-mempoolreplacement"); value && !ValidPolicyTokens(*value, {"fee", "optin", "-optin"})) {
|
|
130
|
+
return util::Error{Untranslated(strprintf("Invalid -mempoolreplacement value: '%s' (expected empty, 0, 1, or fee/optin/-optin tokens separated by ',' or '+')", *value))};
|
|
131
|
+
}
|
|
132
|
+
if (const auto value = argsman.GetArg("-mempooltruc"); value && !ValidPolicyTokens(*value, {"reject", "accept", "enforce", "optin", "-enforce", "-optin", "0"})) {
|
|
133
|
+
return util::Error{Untranslated(strprintf("Invalid -mempooltruc value: '%s' (expected empty, 0, 1, or reject/accept/enforce/optin/-enforce/-optin tokens separated by ',' or '+')", *value))};
|
|
134
|
+
}
|
|
135
|
+
if (const auto value = argsman.GetArg("-permitephemeral"); value && !ValidPolicyTokens(*value, {"anchor", "dust", "send", "reject", "0", "-anchor", "-dust", "-send", "-reject", "-0"})) {
|
|
136
|
+
return util::Error{Untranslated(strprintf("Invalid -permitephemeral value: '%s' (expected empty, 0, 1, or optionally '-' prefixed anchor/dust/send/reject/0 tokens separated by ',' or '+')", *value))};
|
|
137
|
+
}
|
|
138
|
+
if (const auto value = argsman.GetArg("-spkreuse"); value && !value->empty() && *value != "0" && *value != "1" && *value != "allow" && *value != "conflict") {
|
|
139
|
+
return util::Error{Untranslated(strprintf("Invalid -spkreuse value: '%s' (expected allow, conflict, empty, 0, or 1)", *value))};
|
|
140
|
+
}
|
|
141
|
+
return {};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
util::Result<std::pair<int32_t, int>> ParseDustDynamicOpt(std::string_view optstr, const unsigned int max_fee_estimate_blocks)
|
|
145
|
+
{
|
|
146
|
+
if (optstr == "0" || optstr == "off") {
|
|
147
|
+
return std::pair<int32_t, int>(0, DEFAULT_DUST_RELAY_MULTIPLIER);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
int multiplier{DEFAULT_DUST_RELAY_MULTIPLIER};
|
|
151
|
+
if (auto pos = optstr.find('*'); pos != optstr.npos) {
|
|
152
|
+
int64_t parsed;
|
|
153
|
+
if ((!ParseFixedPoint(optstr.substr(0, pos), 3, &parsed)) || parsed > std::numeric_limits<int>::max() || parsed < 1) {
|
|
154
|
+
return util::Error{_("failed to parse multiplier")};
|
|
155
|
+
}
|
|
156
|
+
multiplier = parsed;
|
|
157
|
+
optstr.remove_prefix(pos + 1);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (optstr.starts_with("target:")) {
|
|
161
|
+
const auto val = ToIntegral<uint16_t>(optstr.substr(7));
|
|
162
|
+
if (!val) {
|
|
163
|
+
return util::Error{_("failed to parse target block count")};
|
|
164
|
+
}
|
|
165
|
+
if (*val < 2) {
|
|
166
|
+
return util::Error{_("target must be at least 2 blocks")};
|
|
167
|
+
}
|
|
168
|
+
if (Assume(max_fee_estimate_blocks) && *val > max_fee_estimate_blocks) {
|
|
169
|
+
return util::Error{strprintf(_("target can only be at most %s blocks"), max_fee_estimate_blocks)};
|
|
170
|
+
}
|
|
171
|
+
return std::pair<int32_t, int>(-*val, multiplier);
|
|
172
|
+
} else if (optstr.starts_with("mempool:")) {
|
|
173
|
+
const auto val = ToIntegral<int32_t>(optstr.substr(8));
|
|
174
|
+
if (!val) {
|
|
175
|
+
return util::Error{_("failed to parse mempool position")};
|
|
176
|
+
}
|
|
177
|
+
if (*val < 1) {
|
|
178
|
+
return util::Error{_("mempool position must be at least 1 kB")};
|
|
179
|
+
}
|
|
180
|
+
return std::pair<int32_t, int>(*val, multiplier);
|
|
181
|
+
} else {
|
|
182
|
+
return util::Error{strprintf(_("\"%s\""), optstr)};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
void ApplyPermitEphemeralOption(const common::SettingsValue& value, MemPoolOptions& mempool_opts)
|
|
187
|
+
{
|
|
188
|
+
std::optional<bool> flag_anchor, flag_send, flag_dust;
|
|
189
|
+
const auto text = SettingToString(value).value_or("0");
|
|
190
|
+
if (text.empty() || text == "1") {
|
|
191
|
+
flag_anchor = flag_send = flag_dust = true;
|
|
192
|
+
}
|
|
193
|
+
for (auto& opt : util::SplitString(SettingToString(value).value_or(""), ",+")) {
|
|
194
|
+
bool v{true};
|
|
195
|
+
if (opt.size() && opt[0] == '-') {
|
|
196
|
+
opt.erase(opt.begin());
|
|
197
|
+
v = false;
|
|
198
|
+
}
|
|
199
|
+
if (opt == "anchor") {
|
|
200
|
+
flag_anchor = v;
|
|
201
|
+
} else if (opt == "dust") {
|
|
202
|
+
flag_dust = v;
|
|
203
|
+
} else if (opt == "send") {
|
|
204
|
+
flag_send = v;
|
|
205
|
+
} else if (opt == "reject" || opt == "0") {
|
|
206
|
+
flag_anchor = flag_send = flag_dust = !v;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!flag_send) {
|
|
211
|
+
flag_send = flag_dust.value_or(false) && !flag_anchor.value_or(false);
|
|
212
|
+
}
|
|
213
|
+
if (!flag_dust) {
|
|
214
|
+
flag_dust = flag_send;
|
|
215
|
+
}
|
|
216
|
+
if (!flag_anchor) {
|
|
217
|
+
flag_anchor = true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
mempool_opts.permitephemeral_dust = *flag_dust;
|
|
221
|
+
mempool_opts.permitephemeral_anchor = *flag_anchor;
|
|
222
|
+
mempool_opts.permitephemeral_send = *flag_send;
|
|
223
|
+
}
|
|
224
|
+
|
|
45
225
|
util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainParams& chainparams, MemPoolOptions& mempool_opts)
|
|
46
226
|
{
|
|
227
|
+
if (auto result = ValidateRootsPolicyOptions(argsman); !result) return result;
|
|
47
228
|
mempool_opts.check_ratio = argsman.GetIntArg("-checkmempool", mempool_opts.check_ratio);
|
|
48
229
|
|
|
49
230
|
if (auto mb = argsman.GetIntArg("-maxmempool")) {
|
|
@@ -66,7 +247,6 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
|
|
|
66
247
|
}
|
|
67
248
|
}
|
|
68
249
|
|
|
69
|
-
static_assert(DEFAULT_MIN_RELAY_TX_FEE == DEFAULT_INCREMENTAL_RELAY_FEE);
|
|
70
250
|
if (const auto arg{argsman.GetArg("-minrelaytxfee")}) {
|
|
71
251
|
if (std::optional<CAmount> min_relay_feerate = ParseMoney(*arg)) {
|
|
72
252
|
// High fee check is done afterward in CWallet::Create()
|
|
@@ -90,19 +270,48 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
|
|
|
90
270
|
}
|
|
91
271
|
}
|
|
92
272
|
|
|
273
|
+
mempool_opts.minrelaycoinblocks = argsman.GetIntArg("-minrelaycoinblocks", 0);
|
|
274
|
+
mempool_opts.minrelaymaturity = argsman.GetIntArg("-minrelaymaturity", 0);
|
|
275
|
+
mempool_opts.maxtxlegacysigops = argsman.GetIntArg("-maxtxlegacysigops", mempool_opts.maxtxlegacysigops);
|
|
276
|
+
|
|
277
|
+
mempool_opts.permitbareanchor = argsman.GetBoolArg("-permitbareanchor", mempool_opts.permitbareanchor);
|
|
278
|
+
|
|
279
|
+
mempool_opts.permit_bare_pubkey = argsman.GetBoolArg("-permitbarepubkey", DEFAULT_PERMIT_BAREPUBKEY);
|
|
280
|
+
|
|
93
281
|
mempool_opts.permit_bare_multisig = argsman.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
|
|
94
282
|
|
|
283
|
+
mempool_opts.reject_parasites = argsman.GetBoolArg("-rejectparasites", DEFAULT_REJECT_PARASITES);
|
|
284
|
+
|
|
285
|
+
mempool_opts.reject_tokens = argsman.GetBoolArg("-rejecttokens", DEFAULT_REJECT_TOKENS);
|
|
286
|
+
|
|
287
|
+
|
|
95
288
|
if (argsman.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER)) {
|
|
96
289
|
mempool_opts.max_datacarrier_bytes = argsman.GetIntArg("-datacarriersize", MAX_OP_RETURN_RELAY);
|
|
97
290
|
} else {
|
|
98
291
|
mempool_opts.max_datacarrier_bytes = std::nullopt;
|
|
99
292
|
}
|
|
293
|
+
mempool_opts.datacarrier_fullcount = argsman.GetBoolArg("-datacarrierfullcount", DEFAULT_DATACARRIER_FULLCOUNT);
|
|
294
|
+
mempool_opts.accept_non_std_datacarrier = argsman.GetBoolArg("-acceptnonstddatacarrier", false);
|
|
295
|
+
|
|
296
|
+
mempool_opts.permitbaredatacarrier = argsman.GetBoolArg("-permitbaredatacarrier", mempool_opts.permitbaredatacarrier);
|
|
100
297
|
|
|
101
298
|
mempool_opts.require_standard = !argsman.GetBoolArg("-acceptnonstdtxn", DEFAULT_ACCEPT_NON_STD_TXN);
|
|
102
299
|
if (!chainparams.IsTestChain() && !mempool_opts.require_standard) {
|
|
103
300
|
return util::Error{Untranslated(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.GetChainTypeString()))};
|
|
104
301
|
}
|
|
105
302
|
|
|
303
|
+
mempool_opts.acceptunknownwitness = argsman.GetBoolArg("-acceptunknownwitness", mempool_opts.acceptunknownwitness);
|
|
304
|
+
|
|
305
|
+
if (argsman.IsArgSet("-permitephemeral")) {
|
|
306
|
+
ApplyPermitEphemeralOption(argsman.GetSetting("-permitephemeral"), mempool_opts);
|
|
307
|
+
}
|
|
308
|
+
if (const auto value = argsman.GetArg("-dustdynamic")) {
|
|
309
|
+
// v31's long estimator horizon is 1008 blocks. L8 runtime wiring will
|
|
310
|
+
// additionally validate against the initialized estimator's horizon.
|
|
311
|
+
const auto parsed = ParseDustDynamicOpt(*value, 1008);
|
|
312
|
+
if (!parsed) return util::Error{strprintf(_("Invalid mode for dustdynamic: %s (value: '%s'; expected off or [multiplier*]target:blocks or [multiplier*]mempool:kvB)"), util::ErrorString(parsed), *value)};
|
|
313
|
+
}
|
|
314
|
+
|
|
106
315
|
mempool_opts.persist_v1_dat = argsman.GetBoolArg("-persistmempoolv1", mempool_opts.persist_v1_dat);
|
|
107
316
|
|
|
108
317
|
ApplyArgsManOptions(argsman, mempool_opts.limits);
|