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