From 1051bc39a97d4f96f6dad9e8730658beeaeee58c Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Tue, 18 Aug 2026 14:55:29 -0400 Subject: [PATCH] Implement milestone prompt. --- console/executor.hpp | 1 + console/executor_daemon.cpp | 5 ++- console/executor_runner.cpp | 6 +++ console/executor_store.cpp | 34 +++++++++++++++++ console/localize.hpp | 10 +++++ include/bitcoin/server/parser.hpp | 48 +++++++++++------------ include/bitcoin/server/settings.hpp | 3 ++ src/parser.cpp | 59 ++++++++++++++++------------- 8 files changed, 111 insertions(+), 55 deletions(-) diff --git a/console/executor.hpp b/console/executor.hpp index 5376f508..fbf141fa 100644 --- a/console/executor.hpp +++ b/console/executor.hpp @@ -84,6 +84,7 @@ class executor bool restore_store(bool details=false); bool hot_backup_store(bool details=false); bool cold_backup_store(bool details=false); + bool prompt_milestone() const; // Long-running queries (scans). void scan_flags() const; diff --git a/console/executor_daemon.cpp b/console/executor_daemon.cpp index 003d790c..6bb65dd1 100644 --- a/console/executor_daemon.cpp +++ b/console/executor_daemon.cpp @@ -503,7 +503,8 @@ std::string executor::command_line(const std::filesystem::path& config) NOEXCEPT }; return config.empty() ? element(from_path(module)) : - element(from_path(module)) + element("--" BS_CONFIG_VARIABLE) + + element(from_path(module)) + + element("--" + std::string{ parser::config_variable }) + element(from_path(qualified_path(config))); } @@ -519,7 +520,7 @@ std::string executor::command_line(const std::filesystem::path& config) NOEXCEPT return config.empty() ? (format(R"("%1%")") % from_path(module)).str() : (format(R"("%1%" --%2% "%3%")") % from_path(module) % - BS_CONFIG_VARIABLE % from_path(qualified_path(config))).str(); + parser::config_variable % from_path(qualified_path(config))).str(); } #endif // HAVE_APPLE diff --git a/console/executor_runner.cpp b/console/executor_runner.cpp index bbb6daf8..91377b24 100644 --- a/console/executor_runner.cpp +++ b/console/executor_runner.cpp @@ -138,6 +138,12 @@ bool executor::do_run() return false; } } + else if (!prompt_milestone()) + { + logger(BS_MILESTONE_HALTED); + stopper(BS_NODE_STOPPED); + return false; + } else if (!check_store_path(true) || !create_store(true)) { stopper(BS_NODE_STOPPED); diff --git a/console/executor_store.cpp b/console/executor_store.cpp index 241a0e1c..ca934c8a 100644 --- a/console/executor_store.cpp +++ b/console/executor_store.cpp @@ -62,6 +62,40 @@ bool executor::check_store_path(bool create) const return true; } +bool executor::prompt_milestone() const +{ + const auto& milestone = metadata_.configured.bitcoin.milestone; + if (metadata_.is_configured(settings::milestone)) + { + logger(format(BS_MILESTONE_CONFIGURED) % milestone); + return true; + } + + if (service_ || is_zero(milestone.height())) + return true; + + logger(format(BS_MILESTONE_PROMPT) % milestone); + logger(BS_MILESTONE_CHOICE); + + for (std::string line{}; std::getline(input_, line);) + { + if (canceled()) + return false; + + system::trim(line); + if (line.empty()) + return true; + + if (line == "c") + return false; + + logger(BS_MILESTONE_CHOICE); + } + + // Halt on invalidation, continue when console is unavailable. + return !canceled(); +} + bool executor::create_store(bool details) { logger(BS_INITCHAIN_CREATING); diff --git a/console/localize.hpp b/console/localize.hpp index f336bf06..7bb8f100 100644 --- a/console/localize.hpp +++ b/console/localize.hpp @@ -59,6 +59,16 @@ "Storing genesis block." #define BS_INITCHAIN_DATABASE_INITIALIZE_FAILURE \ "Failure storing genesis block." +#define BS_MILESTONE_CONFIGURED \ + "The configured milestone %1% will bypass validation up to that block." +#define BS_MILESTONE_PROMPT \ + "This is a new store, the default milestone %1% will bypass " \ + "validation up to that block." +#define BS_MILESTONE_CHOICE \ + "Press to continue with the default milestone, or 'c' to close." +#define BS_MILESTONE_HALTED \ + "Startup halted, set or clear bitcoin.milestone in the configuration " \ + "and restart." // --restore #define BS_SNAPSHOT_INVALID \ diff --git a/include/bitcoin/server/parser.hpp b/include/bitcoin/server/parser.hpp index 616205f9..51943483 100644 --- a/include/bitcoin/server/parser.hpp +++ b/include/bitcoin/server/parser.hpp @@ -23,32 +23,6 @@ #include #include -// Not localizable. -#define BS_HELP_VARIABLE "help" -#define BS_HARDWARE_VARIABLE "hardware" -#define BS_SETTINGS_VARIABLE "settings" -#define BS_VERSION_VARIABLE "version" -#define BS_NEWSTORE_VARIABLE "newstore" -#define BS_BACKUP_VARIABLE "backup" -#define BS_RESTORE_VARIABLE "restore" -#define BS_DAEMON_VARIABLE "daemon" -#define BS_USER_VARIABLE "user" - -#define BS_FLAGS_VARIABLE "flags" -#define BS_SLABS_VARIABLE "slabs" -#define BS_BUCKETS_VARIABLE "buckets" -#define BS_COLLISIONS_VARIABLE "collisions" -#define BS_INFORMATION_VARIABLE "information" - -#define BS_GET_VARIABLE "get" -#define BS_PUT_VARIABLE "put" - -// This must be lower case but the env var part can be any case. -#define BS_CONFIG_VARIABLE "config" - -// This must match the case of the env var. -#define BS_ENVIRONMENT_VARIABLE_PREFIX "BS_" - namespace libbitcoin { namespace server { @@ -58,6 +32,28 @@ class BCS_API parser : public system::config::parser { public: + /// Environment variable prefix, case must match the env var. + static constexpr auto environment_prefix = "BS_"; + + /// Command line and environment variable names. + static constexpr auto help_variable = "help"; + static constexpr auto hardware_variable = "hardware"; + static constexpr auto settings_variable = "settings"; + static constexpr auto version_variable = "version"; + static constexpr auto newstore_variable = "newstore"; + static constexpr auto backup_variable = "backup"; + static constexpr auto restore_variable = "restore"; + static constexpr auto daemon_variable = "daemon"; + static constexpr auto user_variable = "user"; + static constexpr auto flags_variable = "flags"; + static constexpr auto slabs_variable = "slabs"; + static constexpr auto buckets_variable = "buckets"; + static constexpr auto collisions_variable = "collisions"; + static constexpr auto information_variable = "information"; + static constexpr auto get_variable = "get"; + static constexpr auto put_variable = "put"; + static constexpr auto config_variable = "config"; + parser(system::chain::selection context, const server::settings::embedded_pages& native, const server::settings::embedded_pages& admin) NOEXCEPT; diff --git a/include/bitcoin/server/settings.hpp b/include/bitcoin/server/settings.hpp index 3d6e7c1e..408d6b48 100644 --- a/include/bitcoin/server/settings.hpp +++ b/include/bitcoin/server/settings.hpp @@ -68,6 +68,9 @@ using span_value = network::http::span_body::value_type; class BCS_API settings { public: + /// Names of settings queried for explicit configuration. + static constexpr auto milestone = "bitcoin.milestone"; + /// References to process embeded resources for html_server. struct embedded_pages { diff --git a/src/parser.cpp b/src/parser.cpp index 992554ae..9097945e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -180,68 +180,74 @@ parser::parser(system::chain::selection context, configured.database.filter_tx.rate = 1; } +// Composes a variable name with its command line shortcut. +static std::string alias(const std::string& variable, char shortcut) +{ + return variable + ',' + shortcut; +} + options_metadata parser::load_options() THROWS { options_metadata description("options"); description.add_options() ( - BS_CONFIG_VARIABLE ",c", + alias(config_variable, 'c').c_str(), value(&configured.file), "Specify path to a configuration settings file." ) // Information. ( - BS_HELP_VARIABLE ",h", + alias(help_variable, 'h').c_str(), value(&configured.help)-> default_value(false)->zero_tokens(), "Display command line options." ) ( - BS_HARDWARE_VARIABLE ",w", + alias(hardware_variable, 'w').c_str(), value(&configured.hardware)-> default_value(false)->zero_tokens(), "Display hardware compatibility." ) ( - BS_SETTINGS_VARIABLE ",s", + alias(settings_variable, 's').c_str(), value(&configured.settings)-> default_value(false)->zero_tokens(), "Display all configuration settings." ) ( - BS_VERSION_VARIABLE ",v", + alias(version_variable, 'v').c_str(), value(&configured.version)-> default_value(false)->zero_tokens(), "Display version information." ) // Actions. ( - BS_NEWSTORE_VARIABLE ",n", + alias(newstore_variable, 'n').c_str(), value(&configured.newstore)-> default_value(false)->zero_tokens(), "Create new store in configured directory." ) ( - BS_BACKUP_VARIABLE ",b", + alias(backup_variable, 'b').c_str(), value(&configured.backup)-> default_value(false)->zero_tokens(), "Backup to a snapshot (can also do live)." ) ( - BS_RESTORE_VARIABLE ",r", + alias(restore_variable, 'r').c_str(), value(&configured.restore)-> default_value(false)->zero_tokens(), "Restore from most recent snapshot." ) // Service. ( - BS_DAEMON_VARIABLE ",d", + alias(daemon_variable, 'd').c_str(), value()->implicit_value(true)-> notifier([&](bool value) { configured.daemon = value; }), "Install ('true') or uninstall ('false') as a system service." ) ( - BS_USER_VARIABLE ",u", + alias(user_variable, 'u').c_str(), value()-> notifier([&](const network::config::credential& value) { configured.user = value; }), @@ -249,44 +255,44 @@ options_metadata parser::load_options() THROWS ) // Chain scans. ( - BS_FLAGS_VARIABLE ",f", + alias(flags_variable, 'f').c_str(), value(&configured.flags)-> default_value(false)->zero_tokens(), "Scan and display all flag transitions." ) ( - BS_SLABS_VARIABLE ",a", + alias(slabs_variable, 'a').c_str(), value(&configured.slabs)-> default_value(false)->zero_tokens(), "Scan and display store slab measures." ) ( - BS_BUCKETS_VARIABLE ",k", + alias(buckets_variable, 'k').c_str(), value(&configured.buckets)-> default_value(false)->zero_tokens(), "Scan and display all bucket densities." ) ( - BS_COLLISIONS_VARIABLE ",l", + alias(collisions_variable, 'l').c_str(), value(&configured.collisions)-> default_value(false)->zero_tokens(), "Scan and display hashmap collision stats (may exceed RAM and result in SIGKILL)." ) ( - BS_INFORMATION_VARIABLE ",i", + alias(information_variable, 'i').c_str(), value(&configured.information)-> default_value(false)->zero_tokens(), "Scan and display store information." ) // Ad-hoc Testing. ( - BS_GET_VARIABLE ",g", + alias(get_variable, 'g').c_str(), value(&configured.get)-> default_value(system::null_hash), "Run built-in read test and display." ) ( - BS_PUT_VARIABLE ",p", + alias(put_variable, 'p').c_str(), value(&configured.put)-> default_value(system::null_hash), "Run built-in write test and display." @@ -309,7 +315,7 @@ options_metadata parser::load_environment() THROWS // For some reason po requires this to be a lower case name. // The case must match the other declarations for it to compose. // This composes with the cmdline options and inits to default path. - BS_CONFIG_VARIABLE, + config_variable, value(&configured.file)->composing() /*->default_value(config_default_path())*/, "The path to the configuration settings file." @@ -543,7 +549,7 @@ options_metadata parser::load_settings() THROWS "The hash:height checkpoint for bip9 bit2 activation, defaults to '0000000000000000000687bca986194dc2c1f949318629b44bb54ec0a94d8244:709632'." ) ( - "bitcoin.milestone", + settings::milestone, value(&configured.bitcoin.milestone), "A block presumed to be valid but not required to be present, defaults to '000000000000000000010b93c9ea1c29fea277383f0f7d1f26de8b5802e885ff:950000'." ) @@ -2044,21 +2050,20 @@ BC_POP_WARNING() try { auto file = false; - variables_map variables; - load_command_variables(variables, argc, argv); - load_environment_variables(variables, BS_ENVIRONMENT_VARIABLE_PREFIX); + load_command_variables(argc, argv); + load_environment_variables(environment_prefix); // Don't load config file if any of these options are specified. - if (!get_option(variables, BS_VERSION_VARIABLE) && - !get_option(variables, BS_SETTINGS_VARIABLE) && - !get_option(variables, BS_HELP_VARIABLE)) + if (!get_option(version_variable) && + !get_option(settings_variable) && + !get_option(help_variable)) { // Returns true if the settings were loaded from a file. - file = load_configuration_variables(variables, BS_CONFIG_VARIABLE); + file = load_configuration_variables(config_variable); } // Update bound variables in metadata.settings. - notify(variables); + notify(variables_); // Clear the config file path if it wasn't used. if (!file)