mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-11 21:20:39 +02:00
Add a belt-and-suspenders feature, limit the amount of memory and cpu possible when unlucky or simply misconfigured. The worst case limit is roughly 400kB * 10,000 = 4GB, regardless of usage pattern. Before this change, sheer volume of broadcasts, mismatches in standardness rules, or simply fee mismatches may result in unbounded growth of memory usage. As the feature may be expanded in the future, explicit bounds helps reasoning going forward.
172 lines
6.5 KiB
C++
172 lines
6.5 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <common/messages.h>
|
|
#include <common/types.h>
|
|
#include <node/types.h>
|
|
#include <policy/fees/block_policy_estimator.h>
|
|
#include <tinyformat.h>
|
|
#include <util/fees.h>
|
|
#include <util/strencodings.h>
|
|
#include <util/string.h>
|
|
#include <util/translation.h>
|
|
|
|
#include <cassert>
|
|
#include <map>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using node::TransactionError;
|
|
using util::Join;
|
|
|
|
namespace common {
|
|
std::string StringForFeeReason(FeeReason reason)
|
|
{
|
|
static const std::map<FeeReason, std::string> fee_reason_strings = {
|
|
{FeeReason::NONE, "None"},
|
|
{FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"},
|
|
{FeeReason::FULL_ESTIMATE, "Target 85% Threshold"},
|
|
{FeeReason::DOUBLE_ESTIMATE, "Double Target 95% Threshold"},
|
|
{FeeReason::CONSERVATIVE, "Conservative Double Target longer horizon"},
|
|
{FeeReason::MEMPOOL_MIN, "Mempool Min Fee"},
|
|
{FeeReason::FALLBACK, "Fallback fee"},
|
|
{FeeReason::REQUIRED, "Minimum Required Fee"},
|
|
};
|
|
auto reason_string = fee_reason_strings.find(reason);
|
|
|
|
if (reason_string == fee_reason_strings.end()) return "Unknown";
|
|
|
|
return reason_string->second;
|
|
}
|
|
|
|
const std::vector<std::pair<std::string, FeeEstimateMode>>& FeeModeMap()
|
|
{
|
|
static const std::vector<std::pair<std::string, FeeEstimateMode>> FEE_MODES = {
|
|
{"unset", FeeEstimateMode::UNSET},
|
|
{"economical", FeeEstimateMode::ECONOMICAL},
|
|
{"conservative", FeeEstimateMode::CONSERVATIVE},
|
|
};
|
|
return FEE_MODES;
|
|
}
|
|
|
|
std::string FeeModeInfo(const std::pair<std::string, FeeEstimateMode>& mode, std::string& default_info)
|
|
{
|
|
switch (mode.second) {
|
|
case FeeEstimateMode::UNSET:
|
|
return strprintf("%s means no mode set (%s). \n", mode.first, default_info);
|
|
case FeeEstimateMode::ECONOMICAL:
|
|
return strprintf("%s estimates use a shorter time horizon, making them more\n"
|
|
"responsive to short-term drops in the prevailing fee market. This mode\n"
|
|
"potentially returns a lower fee rate estimate.\n", mode.first);
|
|
case FeeEstimateMode::CONSERVATIVE:
|
|
return strprintf("%s estimates use a longer time horizon, making them\n"
|
|
"less responsive to short-term drops in the prevailing fee market. This mode\n"
|
|
"potentially returns a higher fee rate estimate.\n", mode.first);
|
|
} // no default case, so the compiler can warn about missing cases
|
|
assert(false);
|
|
}
|
|
|
|
std::string FeeModesDetail(std::string default_info)
|
|
{
|
|
std::string info;
|
|
for (const auto& fee_mode : FeeModeMap()) {
|
|
info += FeeModeInfo(fee_mode, default_info);
|
|
}
|
|
return strprintf("%s \n%s", FeeModes(", "), info);
|
|
}
|
|
|
|
std::string FeeModes(const std::string& delimiter)
|
|
{
|
|
return Join(FeeModeMap(), delimiter, [&](const std::pair<std::string, FeeEstimateMode>& i) { return i.first; });
|
|
}
|
|
|
|
std::string InvalidEstimateModeErrorMessage()
|
|
{
|
|
return "Invalid estimate_mode parameter, must be one of: \"" + FeeModes("\", \"") + "\"";
|
|
}
|
|
|
|
bool FeeModeFromString(std::string_view mode_string, FeeEstimateMode& fee_estimate_mode)
|
|
{
|
|
auto searchkey = ToUpper(mode_string);
|
|
for (const auto& pair : FeeModeMap()) {
|
|
if (ToUpper(pair.first) == searchkey) {
|
|
fee_estimate_mode = pair.second;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bilingual_str PSBTErrorString(PSBTError err)
|
|
{
|
|
switch (err) {
|
|
case PSBTError::MISSING_INPUTS:
|
|
return Untranslated("Inputs missing or spent");
|
|
case PSBTError::SIGHASH_MISMATCH:
|
|
return Untranslated("Specified sighash value does not match value stored in PSBT");
|
|
case PSBTError::EXTERNAL_SIGNER_NOT_FOUND:
|
|
return Untranslated("External signer not found");
|
|
case PSBTError::EXTERNAL_SIGNER_FAILED:
|
|
return Untranslated("External signer failed to sign");
|
|
case PSBTError::UNSUPPORTED:
|
|
return Untranslated("Signer does not support PSBT");
|
|
case PSBTError::INCOMPLETE:
|
|
return Untranslated("Input needs additional signatures or other data");
|
|
case PSBTError::INVALID_TX:
|
|
return Untranslated("The transaction cannot be valid");
|
|
case PSBTError::OK:
|
|
return Untranslated("No errors");
|
|
} // no default case, so the compiler can warn about missing cases
|
|
assert(false);
|
|
}
|
|
|
|
bilingual_str TransactionErrorString(const TransactionError err)
|
|
{
|
|
switch (err) {
|
|
case TransactionError::OK:
|
|
return Untranslated("No error");
|
|
case TransactionError::MISSING_INPUTS:
|
|
return Untranslated("Inputs missing or spent");
|
|
case TransactionError::ALREADY_IN_UTXO_SET:
|
|
return Untranslated("Transaction outputs already in utxo set");
|
|
case TransactionError::MEMPOOL_REJECTED:
|
|
return Untranslated("Transaction rejected by mempool");
|
|
case TransactionError::MEMPOOL_ERROR:
|
|
return Untranslated("Mempool internal error");
|
|
case TransactionError::MAX_FEE_EXCEEDED:
|
|
return Untranslated("Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)");
|
|
case TransactionError::MAX_BURN_EXCEEDED:
|
|
return Untranslated("Unspendable output exceeds maximum configured by user (maxburnamount)");
|
|
case TransactionError::INVALID_PACKAGE:
|
|
return Untranslated("Transaction rejected due to invalid package");
|
|
case TransactionError::PRIVATE_BROADCAST_FULL:
|
|
return Untranslated("Private broadcast queue is full");
|
|
} // no default case, so the compiler can warn about missing cases
|
|
assert(false);
|
|
}
|
|
|
|
bilingual_str ResolveErrMsg(const std::string& optname, const std::string& strBind)
|
|
{
|
|
return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
|
|
}
|
|
|
|
bilingual_str InvalidPortErrMsg(const std::string& optname, const std::string& invalid_value)
|
|
{
|
|
return strprintf(_("Invalid port specified in %s: '%s'"), optname, invalid_value);
|
|
}
|
|
|
|
bilingual_str AmountHighWarn(const std::string& optname)
|
|
{
|
|
return strprintf(_("%s is set very high!"), optname);
|
|
}
|
|
|
|
bilingual_str AmountErrMsg(const std::string& optname, const std::string& strValue)
|
|
{
|
|
return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
|
|
}
|
|
} // namespace common
|