mirror of https://github.com/bitcoin/bitcoin.git
Merge 8aa49a4b82
into b510893d00
This commit is contained in:
commit
fc39f66e50
|
@ -1207,16 +1207,21 @@ static void SetGenerateToAddressArgs(const std::string& address, std::vector<std
|
|||
args.emplace(args.begin() + 1, address);
|
||||
}
|
||||
|
||||
static int CommandLineRPC(int argc, char *argv[])
|
||||
std::vector<std::string> GetCommandArgs() {
|
||||
std::optional<const ArgsManager::Command> command = gArgs.GetCommand();
|
||||
|
||||
// Return command args if command is present, otherwise return an empty vector
|
||||
if (command.has_value()) {
|
||||
return command->args;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static int CommandLineRPC()
|
||||
{
|
||||
std::string strPrint;
|
||||
int nRet = 0;
|
||||
try {
|
||||
// Skip switches
|
||||
while (argc > 1 && IsSwitchChar(argv[1][0])) {
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
std::string rpcPass;
|
||||
if (gArgs.GetBoolArg("-stdinrpcpass", false)) {
|
||||
NO_STDIN_ECHO();
|
||||
|
@ -1232,7 +1237,7 @@ static int CommandLineRPC(int argc, char *argv[])
|
|||
}
|
||||
gArgs.ForceSetArg("-rpcpassword", rpcPass);
|
||||
}
|
||||
std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
|
||||
std::vector<std::string> args = GetCommandArgs();
|
||||
if (gArgs.GetBoolArg("-stdinwalletpassphrase", false)) {
|
||||
NO_STDIN_ECHO();
|
||||
std::string walletPass;
|
||||
|
@ -1354,7 +1359,7 @@ MAIN_FUNCTION
|
|||
|
||||
int ret = EXIT_FAILURE;
|
||||
try {
|
||||
ret = CommandLineRPC(argc, argv);
|
||||
ret = CommandLineRPC();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
PrintExceptionContinue(&e, "CommandLineRPC()");
|
||||
|
|
|
@ -176,6 +176,42 @@ void ArgsManager::SelectConfigNetwork(const std::string& network)
|
|||
m_network = network;
|
||||
}
|
||||
|
||||
bool IsArgNumber(const char* arg) {
|
||||
double dummy{};
|
||||
return ParseDouble(arg, &dummy);
|
||||
}
|
||||
|
||||
bool ArgsManager::ProcessOptionKey(std::string& key, std::optional<std::string>& val, std::string& error) {
|
||||
|
||||
std::string original_input = key; // Capture the original key
|
||||
if (val) original_input += "=" + *val; // Append =value if it exists
|
||||
|
||||
// Transform --foo to -foo
|
||||
if (key.length() > 1 && key[1] == '-')
|
||||
key.erase(0, 1);
|
||||
|
||||
// Transform -foo to foo
|
||||
key.erase(0, 1);
|
||||
|
||||
KeyInfo keyinfo = InterpretKey(key);
|
||||
std::optional<unsigned int> flags = GetArgFlags('-' + keyinfo.name);
|
||||
// Unknown command line options and command line options with dot characters
|
||||
// (which are returned from InterpretKey with nonempty section strings)are not valid.
|
||||
if (!flags || !keyinfo.section.empty()) {
|
||||
error = strprintf("Invalid parameter %s", original_input);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<common::SettingsValue> value = InterpretValue(keyinfo, val ? &*val : nullptr, *flags, error);
|
||||
if (!value) return false;
|
||||
|
||||
// Store the option
|
||||
LOCK(cs_args);
|
||||
m_settings.command_line_options[keyinfo.name].push_back(*value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
|
||||
{
|
||||
LOCK(cs_args);
|
||||
|
@ -217,32 +253,28 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
|
|||
m_command.push_back(key);
|
||||
while (++i < argc) {
|
||||
// The remaining args are command args
|
||||
m_command.emplace_back(argv[i]);
|
||||
if (argv[i][0] == '-' && !IsArgNumber(argv[i])) {
|
||||
// except it starts with dash "-" (and it's not a number) then will check if it's a valid option
|
||||
key = argv[i];
|
||||
val.reset();
|
||||
is_index = key.find('=');
|
||||
if (is_index != std::string::npos) {
|
||||
val = key.substr(is_index + 1);
|
||||
key.erase(is_index);
|
||||
}
|
||||
if (!ProcessOptionKey(key, val, error)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
m_command.emplace_back(argv[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Transform --foo to -foo
|
||||
if (key.length() > 1 && key[1] == '-')
|
||||
key.erase(0, 1);
|
||||
|
||||
// Transform -foo to foo
|
||||
key.erase(0, 1);
|
||||
KeyInfo keyinfo = InterpretKey(key);
|
||||
std::optional<unsigned int> flags = GetArgFlags('-' + keyinfo.name);
|
||||
|
||||
// Unknown command line options and command line options with dot
|
||||
// characters (which are returned from InterpretKey with nonempty
|
||||
// section strings) are not valid.
|
||||
if (!flags || !keyinfo.section.empty()) {
|
||||
error = strprintf("Invalid parameter %s", argv[i]);
|
||||
if (!ProcessOptionKey(key, val, error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<common::SettingsValue> value = InterpretValue(keyinfo, val ? &*val : nullptr, *flags, error);
|
||||
if (!value) return false;
|
||||
|
||||
m_settings.command_line_options[keyinfo.name].push_back(*value);
|
||||
}
|
||||
|
||||
// we do not allow -includeconf from command line, only -noincludeconf
|
||||
|
|
|
@ -446,6 +446,8 @@ private:
|
|||
const std::string& prefix,
|
||||
const std::string& section,
|
||||
const std::map<std::string, std::vector<common::SettingsValue>>& args) const;
|
||||
|
||||
bool ProcessOptionKey(std::string& key, std::optional<std::string>& val, std::string& error);
|
||||
};
|
||||
|
||||
extern ArgsManager gArgs;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2011-2022 The Bitcoin Core developers
|
||||
// Copyright (c) 2011-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.
|
||||
|
||||
|
@ -227,13 +227,13 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters)
|
|||
|
||||
BOOST_CHECK(testArgs.ParseParameters(7, argv_test, error));
|
||||
// expectation: -ignored is ignored (program name argument),
|
||||
// -a, -b and -ccc end up in map, -d ignored because it is after
|
||||
// a non-option argument (non-GNU option parsing)
|
||||
BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 3 && testArgs.m_settings.ro_config.empty());
|
||||
// -a, -b and -ccc end up in map, also -d since the parser detects
|
||||
// [options] after a non-option argument (GNU-style option parsing)
|
||||
BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 4 && testArgs.m_settings.ro_config.empty());
|
||||
BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc")
|
||||
&& !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d"));
|
||||
&& !testArgs.IsArgSet("f") && testArgs.IsArgSet("-d"));
|
||||
BOOST_CHECK(testArgs.m_settings.command_line_options.count("a") && testArgs.m_settings.command_line_options.count("b") && testArgs.m_settings.command_line_options.count("ccc")
|
||||
&& !testArgs.m_settings.command_line_options.count("f") && !testArgs.m_settings.command_line_options.count("d"));
|
||||
&& !testArgs.m_settings.command_line_options.count("f") && testArgs.m_settings.command_line_options.count("d"));
|
||||
|
||||
BOOST_CHECK(testArgs.m_settings.command_line_options["a"].size() == 1);
|
||||
BOOST_CHECK(testArgs.m_settings.command_line_options["a"].front().get_str() == "");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
||||
// 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.
|
||||
|
||||
|
@ -55,6 +55,9 @@ FUZZ_TARGET(parse_numbers)
|
|||
|
||||
(void)ParseMoney(random_string);
|
||||
|
||||
double d;
|
||||
(void)ParseDouble(random_string, &d);
|
||||
|
||||
(void)LocaleIndependentAtoi<int>(random_string);
|
||||
|
||||
int64_t i64;
|
||||
|
|
|
@ -835,6 +835,32 @@ BOOST_AUTO_TEST_CASE(test_LocaleIndependentAtoi)
|
|||
BOOST_CHECK_EQUAL(LocaleIndependentAtoi<uint8_t>("256"), 255U);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_ParseDouble)
|
||||
{
|
||||
double n;
|
||||
// Valid values
|
||||
BOOST_CHECK(ParseDouble("1234", nullptr));
|
||||
BOOST_CHECK(ParseDouble("0", &n) && n == 0.0);
|
||||
BOOST_CHECK(ParseDouble("1234", &n) && n == 1234.0);
|
||||
BOOST_CHECK(ParseDouble("01234", &n) && n == 1234.0); // no octal
|
||||
BOOST_CHECK(ParseDouble("2147483647", &n) && n == 2147483647.0);
|
||||
BOOST_CHECK(ParseDouble("-2147483648", &n) && n == -2147483648.0);
|
||||
BOOST_CHECK(ParseDouble("-1234", &n) && n == -1234.0);
|
||||
BOOST_CHECK(ParseDouble("1e6", &n) && n == 1e6);
|
||||
BOOST_CHECK(ParseDouble("-1e6", &n) && n == -1e6);
|
||||
// Invalid values
|
||||
BOOST_CHECK(!ParseDouble("", &n));
|
||||
BOOST_CHECK(!ParseDouble(" 1", &n)); // no padding inside
|
||||
BOOST_CHECK(!ParseDouble("1 ", &n));
|
||||
BOOST_CHECK(!ParseDouble("1a", &n));
|
||||
BOOST_CHECK(!ParseDouble("aap", &n));
|
||||
BOOST_CHECK(!ParseDouble("0x1", &n)); // no hex
|
||||
BOOST_CHECK(!ParseDouble(STRING_WITH_EMBEDDED_NULL_CHAR, &n));
|
||||
// Overflow and underflow
|
||||
BOOST_CHECK(!ParseDouble("-1e10000", nullptr));
|
||||
BOOST_CHECK(!ParseDouble("1e10000", nullptr));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_FormatParagraph)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(FormatParagraph("", 79, 0), "");
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using util::ContainsNoNUL;
|
||||
|
||||
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
static const std::string SAFE_CHARS[] =
|
||||
|
@ -199,6 +201,31 @@ std::optional<std::vector<unsigned char>> DecodeBase32(std::string_view str)
|
|||
return ret;
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool ParsePrechecks(const std::string& str)
|
||||
{
|
||||
if (str.empty()) // No empty string allowed
|
||||
return false;
|
||||
if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size()-1]))) // No padding allowed
|
||||
return false;
|
||||
if (!ContainsNoNUL(str)) // No embedded NUL characters allowed
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseDouble(const std::string& str, double *out)
|
||||
{
|
||||
if (!ParsePrechecks(str))
|
||||
return false;
|
||||
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
|
||||
return false;
|
||||
std::istringstream text(str);
|
||||
text.imbue(std::locale::classic());
|
||||
double result;
|
||||
text >> result;
|
||||
if(out) *out = result;
|
||||
return text.eof() && !text.fail();
|
||||
}
|
||||
|
||||
std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
|
||||
{
|
||||
assert(width >= indent);
|
||||
|
|
|
@ -186,6 +186,13 @@ std::optional<T> ToIntegral(std::string_view str)
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to double with strict parse error feedback.
|
||||
* @returns true if the entire string could be parsed as valid double,
|
||||
* false if not the entire string could be parsed or when overflow or underflow occurred.
|
||||
*/
|
||||
[[nodiscard]] bool ParseDouble(const std::string& str, double *out);
|
||||
|
||||
/**
|
||||
* Format a paragraph of text to a fixed width, adding spaces for
|
||||
* indentation to any added line.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2017-2022 The Bitcoin Core developers
|
||||
# Copyright (c) 2017-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.
|
||||
"""Test bitcoin-cli"""
|
||||
|
@ -352,6 +352,7 @@ class TestBitcoinCli(BitcoinTestFramework):
|
|||
self.nodes[0].loadwallet(wallets[2])
|
||||
n3 = 4
|
||||
n4 = 10
|
||||
n5 = 5
|
||||
blocks = self.nodes[0].getblockcount()
|
||||
|
||||
self.log.info('Test -generate -rpcwallet=<filename> raise RPC error')
|
||||
|
@ -386,9 +387,15 @@ class TestBitcoinCli(BitcoinTestFramework):
|
|||
assert_raises_rpc_error(-19, WALLET_NOT_SPECIFIED, self.nodes[0].cli('-generate', 'foo').echo)
|
||||
assert_raises_rpc_error(-19, WALLET_NOT_SPECIFIED, self.nodes[0].cli('-generate', 0).echo)
|
||||
assert_raises_rpc_error(-19, WALLET_NOT_SPECIFIED, self.nodes[0].cli('-generate', 1, 2, 3).echo)
|
||||
|
||||
self.log.info('Test -generate passing -rpcwallet after the nblocks arg')
|
||||
generate = self.nodes[0].cli('-generate', n5, rpcwallet2).send_cli()
|
||||
assert_equal(set(generate.keys()), {'address', 'blocks'})
|
||||
assert_equal(len(generate["blocks"]), n5)
|
||||
assert_equal(self.nodes[0].getblockcount(), blocks + 1 + n3 + n4 + n5)
|
||||
else:
|
||||
self.log.info("*** Wallet not compiled; cli getwalletinfo and -getinfo wallet tests skipped")
|
||||
self.generate(self.nodes[0], 25) # maintain block parity with the wallet_compiled conditional branch
|
||||
self.generate(self.nodes[0], 30) # maintain block parity with the wallet_compiled conditional branch
|
||||
|
||||
self.test_netinfo()
|
||||
|
||||
|
@ -402,7 +409,7 @@ class TestBitcoinCli(BitcoinTestFramework):
|
|||
self.nodes[0].wait_for_cookie_credentials() # ensure cookie file is available to avoid race condition
|
||||
blocks = self.nodes[0].cli('-rpcwait').send_cli('getblockcount')
|
||||
self.nodes[0].wait_for_rpc_connection()
|
||||
assert_equal(blocks, BLOCKS + 25)
|
||||
assert_equal(blocks, BLOCKS + 30)
|
||||
|
||||
self.log.info("Test -rpcwait option waits at most -rpcwaittimeout seconds for startup")
|
||||
self.stop_node(0) # stop the node so we time out
|
||||
|
|
Loading…
Reference in New Issue