diff --git a/src/init.cpp b/src/init.cpp
index 78944e34c..e3008d837 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,2931 +1,2933 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2018 The Bitcoin Core developers
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 #if defined(HAVE_CONFIG_H)
 #include <config/bitcoin-config.h>
 #endif
 
 #include <init.h>
 
 #include <kernel/mempool_persist.h>
 #include <kernel/validation_cache_sizes.h>
 
 #include <addrman.h>
 #include <avalanche/avalanche.h>
 #include <avalanche/processor.h>
 #include <avalanche/proof.h> // For AVALANCHE_LEGACY_PROOF_DEFAULT
 #include <avalanche/validation.h>
 #include <avalanche/voterecord.h> // For AVALANCHE_VOTE_STALE_*
 #include <banman.h>
 #include <blockfilter.h>
 #include <chain.h>
 #include <chainparams.h>
 #include <compat/sanity.h>
 #include <config.h>
 #include <consensus/amount.h>
 #include <currencyunit.h>
 #include <flatfile.h>
 #include <fs.h>
 #include <hash.h>
 #include <httprpc.h>
 #include <httpserver.h>
 #include <index/blockfilterindex.h>
 #include <index/coinstatsindex.h>
 #include <index/txindex.h>
 #include <init/common.h>
 #include <interfaces/chain.h>
 #include <interfaces/node.h>
 #include <mapport.h>
 #include <mempool_args.h>
 #include <net.h>
 #include <net_permissions.h>
 #include <net_processing.h>
 #include <netbase.h>
 #include <node/blockstorage.h>
 #include <node/caches.h>
 #include <node/chainstate.h>
 #include <node/chainstatemanager_args.h>
 #include <node/context.h>
 #include <node/mempool_persist_args.h>
 #include <node/miner.h>
 #include <node/ui_interface.h>
 #include <node/validation_cache_args.h>
 #include <policy/policy.h>
 #include <policy/settings.h>
 #include <rpc/blockchain.h>
 #include <rpc/register.h>
 #include <rpc/server.h>
 #include <rpc/util.h>
 #include <scheduler.h>
 #include <script/scriptcache.h>
 #include <script/sigcache.h>
 #include <script/standard.h>
 #include <shutdown.h>
 #include <sync.h>
 #include <timedata.h>
 #include <torcontrol.h>
 #include <txdb.h>
 #include <txmempool.h>
 #include <txorphanage.h>
 #include <util/asmap.h>
 #include <util/check.h>
 #include <util/moneystr.h>
 #include <util/string.h>
 #include <util/thread.h>
 #include <util/threadnames.h>
 #include <util/translation.h>
 #include <validation.h>
 #include <validationinterface.h>
 #include <walletinitinterface.h>
 
 #include <boost/signals2/signal.hpp>
 
 #if ENABLE_CHRONIK
 #include <chronik-cpp/chronik.h>
 #endif
 
 #if ENABLE_ZMQ
 #include <zmq/zmqabstractnotifier.h>
 #include <zmq/zmqnotificationinterface.h>
 #include <zmq/zmqrpc.h>
 #endif
 
 #ifndef WIN32
 #include <cerrno>
 #include <csignal>
 #include <sys/stat.h>
 #endif
 #include <algorithm>
 #include <condition_variable>
 #include <cstdint>
 #include <cstdio>
 #include <fstream>
 #include <functional>
 #include <set>
 #include <string>
 #include <thread>
 #include <vector>
 
 using kernel::DumpMempool;
 using kernel::ValidationCacheSizes;
 
 using node::ApplyArgsManOptions;
 using node::CacheSizes;
 using node::CalculateCacheSizes;
 using node::CleanupBlockRevFiles;
 using node::DEFAULT_PERSIST_MEMPOOL;
 using node::DEFAULT_STOPAFTERBLOCKIMPORT;
 using node::fPruneMode;
 using node::fReindex;
 using node::LoadChainstate;
 using node::MempoolPath;
 using node::NodeContext;
 using node::nPruneTarget;
 using node::ShouldPersistMempool;
 using node::ThreadImport;
 using node::VerifyLoadedChainstate;
 
 static const bool DEFAULT_PROXYRANDOMIZE = true;
 static const bool DEFAULT_REST_ENABLE = false;
 static constexpr bool DEFAULT_CHRONIK = false;
 
 #ifdef WIN32
 // Win32 LevelDB doesn't use filedescriptors, and the ones used for accessing
 // block files don't count towards the fd_set size limit anyway.
 #define MIN_CORE_FILEDESCRIPTORS 0
 #else
 #define MIN_CORE_FILEDESCRIPTORS 150
 #endif
 
 static const char *DEFAULT_ASMAP_FILENAME = "ip_asn.map";
 
 /**
  * The PID file facilities.
  */
 static const char *BITCOIN_PID_FILENAME = "bitcoind.pid";
 
 static fs::path GetPidFile(const ArgsManager &args) {
     return AbsPathForConfigVal(args.GetPathArg("-pid", BITCOIN_PID_FILENAME));
 }
 
 [[nodiscard]] static bool CreatePidFile(const ArgsManager &args) {
     std::ofstream file{GetPidFile(args)};
     if (file) {
 #ifdef WIN32
         tfm::format(file, "%d\n", GetCurrentProcessId());
 #else
         tfm::format(file, "%d\n", getpid());
 #endif
         return true;
     } else {
         return InitError(strprintf(_("Unable to create the PID file '%s': %s"),
                                    fs::PathToString(GetPidFile(args)),
                                    std::strerror(errno)));
     }
 }
 
 //////////////////////////////////////////////////////////////////////////////
 //
 // Shutdown
 //
 
 //
 // Thread management and startup/shutdown:
 //
 // The network-processing threads are all part of a thread group created by
 // AppInit() or the Qt main() function.
 //
 // A clean exit happens when StartShutdown() or the SIGTERM signal handler sets
 // fRequestShutdown, which makes main thread's WaitForShutdown() interrupts the
 // thread group.
 // And then, WaitForShutdown() makes all other on-going threads in the thread
 // group join the main thread.
 // Shutdown() is then called to clean up database connections, and stop other
 // threads that should only be stopped after the main network-processing threads
 // have exited.
 //
 // Shutdown for Qt is very similar, only it uses a QTimer to detect
 // ShutdownRequested() getting set, and then does the normal Qt shutdown thing.
 //
 
 void Interrupt(NodeContext &node) {
     InterruptHTTPServer();
     InterruptHTTPRPC();
     InterruptRPC();
     InterruptREST();
     InterruptTorControl();
     InterruptMapPort();
     if (g_avalanche) {
         // Avalanche needs to be stopped before we interrupt the thread group as
         // the scheduler will stop working then.
         g_avalanche->stopEventLoop();
     }
     if (node.connman) {
         node.connman->Interrupt();
     }
     if (g_txindex) {
         g_txindex->Interrupt();
     }
     ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Interrupt(); });
     if (g_coin_stats_index) {
         g_coin_stats_index->Interrupt();
     }
 }
 
 void Shutdown(NodeContext &node) {
     static Mutex g_shutdown_mutex;
     TRY_LOCK(g_shutdown_mutex, lock_shutdown);
     if (!lock_shutdown) {
         return;
     }
     LogPrintf("%s: In progress...\n", __func__);
     Assert(node.args);
 
     /// Note: Shutdown() must be able to handle cases in which initialization
     /// failed part of the way, for example if the data directory was found to
     /// be locked. Be sure that anything that writes files or flushes caches
     /// only does this if the respective module was initialized.
     util::ThreadRename("shutoff");
     if (node.mempool) {
         node.mempool->AddTransactionsUpdated(1);
     }
 
     StopHTTPRPC();
     StopREST();
     StopRPC();
     StopHTTPServer();
     for (const auto &client : node.chain_clients) {
         client->flush();
     }
     StopMapPort();
 
     // Because avalanche and the network depend on each other, it is important
     // to shut them down in this order:
     // 1. Stop avalanche event loop.
     // 2. Shutdown network processing.
     // 3. Destroy avalanche::Processor.
     // 4. Destroy CConnman
     if (g_avalanche) {
         g_avalanche->stopEventLoop();
     }
 
     // Because these depend on each-other, we make sure that neither can be
     // using the other before destroying them.
     if (node.peerman) {
         UnregisterValidationInterface(node.peerman.get());
     }
     if (node.connman) {
         node.connman->Stop();
     }
 
     StopTorControl();
 
     // After everything has been shut down, but before things get flushed, stop
     // the CScheduler/checkqueue, scheduler and load block thread.
     if (node.scheduler) {
         node.scheduler->stop();
     }
     if (node.chainman && node.chainman->m_load_block.joinable()) {
         node.chainman->m_load_block.join();
     }
     StopScriptCheckWorkerThreads();
 
     // After the threads that potentially access these pointers have been
     // stopped, destruct and reset all to nullptr.
     node.peerman.reset();
 
     // Destroy various global instances
     g_avalanche.reset();
     node.connman.reset();
     node.banman.reset();
     node.addrman.reset();
 
     if (node.mempool && node.mempool->GetLoadTried() &&
         ShouldPersistMempool(*node.args)) {
         DumpMempool(*node.mempool, MempoolPath(*node.args));
     }
 
     // FlushStateToDisk generates a ChainStateFlushed callback, which we should
     // avoid missing
     if (node.chainman) {
         LOCK(cs_main);
         for (Chainstate *chainstate : node.chainman->GetAll()) {
             if (chainstate->CanFlushToDisk()) {
                 chainstate->ForceFlushStateToDisk();
             }
         }
     }
 
     // After there are no more peers/RPC left to give us new data which may
     // generate CValidationInterface callbacks, flush them...
     GetMainSignals().FlushBackgroundCallbacks();
 
 #if ENABLE_CHRONIK
-    chronik::Stop();
+    if (node.args->GetBoolArg("-chronik", DEFAULT_CHRONIK)) {
+        chronik::Stop();
+    }
 #endif
 
     // Stop and delete all indexes only after flushing background callbacks.
     if (g_txindex) {
         g_txindex->Stop();
         g_txindex.reset();
     }
     if (g_coin_stats_index) {
         g_coin_stats_index->Stop();
         g_coin_stats_index.reset();
     }
     ForEachBlockFilterIndex([](BlockFilterIndex &index) { index.Stop(); });
     DestroyAllBlockFilterIndexes();
 
     // Any future callbacks will be dropped. This should absolutely be safe - if
     // missing a callback results in an unrecoverable situation, unclean
     // shutdown would too. The only reason to do the above flushes is to let the
     // wallet catch up with our current chain to avoid any strange pruning edge
     // cases and make next startup faster by avoiding rescan.
 
     if (node.chainman) {
         LOCK(cs_main);
         for (Chainstate *chainstate : node.chainman->GetAll()) {
             if (chainstate->CanFlushToDisk()) {
                 chainstate->ForceFlushStateToDisk();
                 chainstate->ResetCoinsViews();
             }
         }
     }
     for (const auto &client : node.chain_clients) {
         client->stop();
     }
 
 #if ENABLE_ZMQ
     if (g_zmq_notification_interface) {
         UnregisterValidationInterface(g_zmq_notification_interface);
         delete g_zmq_notification_interface;
         g_zmq_notification_interface = nullptr;
     }
 #endif
 
     node.chain_clients.clear();
     UnregisterAllValidationInterfaces();
     GetMainSignals().UnregisterBackgroundSignalScheduler();
     init::UnsetGlobals();
     node.mempool.reset();
     node.chainman.reset();
     node.scheduler.reset();
 
     try {
         if (!fs::remove(GetPidFile(*node.args))) {
             LogPrintf("%s: Unable to remove PID file: File does not exist\n",
                       __func__);
         }
     } catch (const fs::filesystem_error &e) {
         LogPrintf("%s: Unable to remove PID file: %s\n", __func__,
                   fsbridge::get_filesystem_error_message(e));
     }
 
     LogPrintf("%s: done\n", __func__);
 }
 
 /**
  * Signal handlers are very limited in what they are allowed to do.
  * The execution context the handler is invoked in is not guaranteed,
  * so we restrict handler operations to just touching variables:
  */
 #ifndef WIN32
 static void HandleSIGTERM(int) {
     StartShutdown();
 }
 
 static void HandleSIGHUP(int) {
     LogInstance().m_reopen_file = true;
 }
 #else
 static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType) {
     StartShutdown();
     Sleep(INFINITE);
     return true;
 }
 #endif
 
 #ifndef WIN32
 static void registerSignalHandler(int signal, void (*handler)(int)) {
     struct sigaction sa;
     sa.sa_handler = handler;
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = 0;
     sigaction(signal, &sa, NULL);
 }
 #endif
 
 static boost::signals2::connection rpc_notify_block_change_connection;
 static void OnRPCStarted() {
     rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(
         std::bind(RPCNotifyBlockChange, std::placeholders::_2));
 }
 
 static void OnRPCStopped() {
     rpc_notify_block_change_connection.disconnect();
     RPCNotifyBlockChange(nullptr);
     g_best_block_cv.notify_all();
     LogPrint(BCLog::RPC, "RPC stopped.\n");
 }
 
 void SetupServerArgs(NodeContext &node) {
     assert(!node.args);
     node.args = &gArgs;
     ArgsManager &argsman = *node.args;
 
     SetupHelpOptions(argsman);
     SetupCurrencyUnitOptions(argsman);
     // server-only for now
     argsman.AddArg("-help-debug",
                    "Print help message with debugging options and exit", false,
                    OptionsCategory::DEBUG_TEST);
 
     init::AddLoggingArgs(argsman);
 
     const auto defaultBaseParams =
         CreateBaseChainParams(CBaseChainParams::MAIN);
     const auto testnetBaseParams =
         CreateBaseChainParams(CBaseChainParams::TESTNET);
     const auto regtestBaseParams =
         CreateBaseChainParams(CBaseChainParams::REGTEST);
     const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN);
     const auto testnetChainParams =
         CreateChainParams(CBaseChainParams::TESTNET);
     const auto regtestChainParams =
         CreateChainParams(CBaseChainParams::REGTEST);
 
     // Hidden Options
     std::vector<std::string> hidden_args = {
         "-dbcrashratio",
         "-forcecompactdb",
         "-maxaddrtosend",
         "-parkdeepreorg",
         "-automaticunparking",
         "-replayprotectionactivationtime",
         "-enableminerfund",
         "-chronikallowpause",
         // GUI args. These will be overwritten by SetupUIArgs for the GUI
         "-allowselfsignedrootcertificates",
         "-choosedatadir",
         "-lang=<lang>",
         "-min",
         "-resetguisettings",
         "-rootcertificates=<file>",
         "-splash",
         "-uiplatform",
         // TODO remove after the May. 2024 upgrade
         "-leekuanyewactivationtime",
     };
 
     // Set all of the args and their help
     // When adding new options to the categories, please keep and ensure
     // alphabetical ordering. Do not translate _(...) -help-debug options, Many
     // technical terms, and only a very small audience, so is unnecessary stress
     // to translators.
     argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY,
                    OptionsCategory::OPTIONS);
 #if defined(HAVE_SYSTEM)
     argsman.AddArg(
         "-alertnotify=<cmd>",
         "Execute command when a relevant alert is received or we see "
         "a really long fork (%s in cmd is replaced by message)",
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 #endif
     argsman.AddArg(
         "-assumevalid=<hex>",
         strprintf(
             "If this block is in the chain assume that it and its ancestors "
             "are valid and potentially skip their script verification (0 to "
             "verify all, default: %s, testnet: %s)",
             defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(),
             testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()),
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg("-blocksdir=<dir>",
                    "Specify directory to hold blocks subdirectory for *.dat "
                    "files (default: <datadir>)",
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg("-fastprune",
                    "Use smaller block files and lower minimum prune height for "
                    "testing purposes",
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
 #if defined(HAVE_SYSTEM)
     argsman.AddArg("-blocknotify=<cmd>",
                    "Execute command when the best block changes (%s in cmd is "
                    "replaced by block hash)",
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 #endif
     argsman.AddArg("-blockreconstructionextratxn=<n>",
                    strprintf("Extra transactions to keep in memory for compact "
                              "block reconstructions (default: %u)",
                              DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN),
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-blocksonly",
         strprintf("Whether to reject transactions from network peers.  "
                   "Automatic broadcast and rebroadcast of any transactions "
                   "from inbound peers is disabled, unless the peer has the "
                   "'forcerelay' permission. RPC transactions are"
                   " not affected. (default: %u)",
                   DEFAULT_BLOCKSONLY),
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg("-coinstatsindex",
                    strprintf("Maintain coinstats index used by the "
                              "gettxoutsetinfo RPC (default: %u)",
                              DEFAULT_COINSTATSINDEX),
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-conf=<file>",
         strprintf("Specify path to read-only configuration file. Relative "
                   "paths will be prefixed by datadir location. (default: %s)",
                   BITCOIN_CONF_FILENAME),
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg("-datadir=<dir>", "Specify data directory",
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-dbbatchsize",
         strprintf("Maximum database write batch size in bytes (default: %u)",
                   DEFAULT_DB_BATCH_SIZE),
         ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
         OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-dbcache=<n>",
         strprintf("Set database cache size in MiB (%d to %d, default: %d)",
                   MIN_DB_CACHE_MB, MAX_DB_CACHE_MB, DEFAULT_DB_CACHE_MB),
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-includeconf=<file>",
         "Specify additional configuration file, relative to the -datadir path "
         "(only useable from configuration file, not command line)",
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg("-loadblock=<file>",
                    "Imports blocks from external file on startup",
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg("-maxmempool=<n>",
                    strprintf("Keep the transaction memory pool below <n> "
                              "megabytes (default: %u)",
                              DEFAULT_MAX_MEMPOOL_SIZE_MB),
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg("-maxorphantx=<n>",
                    strprintf("Keep at most <n> unconnectable transactions in "
                              "memory (default: %u)",
                              DEFAULT_MAX_ORPHAN_TRANSACTIONS),
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg("-mempoolexpiry=<n>",
                    strprintf("Do not keep transactions in the mempool longer "
                              "than <n> hours (default: %u)",
                              DEFAULT_MEMPOOL_EXPIRY_HOURS),
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-minimumchainwork=<hex>",
         strprintf(
             "Minimum work assumed to exist on a valid chain in hex "
             "(default: %s, testnet: %s)",
             defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(),
             testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()),
         ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
         OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-par=<n>",
         strprintf("Set the number of script verification threads (%u to %d, 0 "
                   "= auto, <0 = leave that many cores free, default: %d)",
                   -GetNumCores(), MAX_SCRIPTCHECK_THREADS,
                   DEFAULT_SCRIPTCHECK_THREADS),
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg("-persistmempool",
                    strprintf("Whether to save the mempool on shutdown and load "
                              "on restart (default: %u)",
                              DEFAULT_PERSIST_MEMPOOL),
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-pid=<file>",
         strprintf("Specify pid file. Relative paths will be prefixed "
                   "by a net-specific datadir location. (default: %s)",
                   BITCOIN_PID_FILENAME),
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-prune=<n>",
         strprintf("Reduce storage requirements by enabling pruning (deleting) "
                   "of old blocks. This allows the pruneblockchain RPC to be "
                   "called to delete specific blocks, and enables automatic "
                   "pruning of old blocks if a target size in MiB is provided. "
                   "This mode is incompatible with -txindex, -coinstatsindex "
                   "and -rescan. Warning: Reverting this setting requires "
                   "re-downloading the entire blockchain. (default: 0 = disable "
                   "pruning blocks, 1 = allow manual pruning via RPC, >=%u = "
                   "automatically prune block files to stay under the specified "
                   "target size in MiB)",
                   MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024),
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-reindex-chainstate",
         "Rebuild chain state from the currently indexed blocks. When "
         "in pruning mode or if blocks on disk might be corrupted, use "
         "full -reindex instead.",
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-reindex",
         "Rebuild chain state and block index from the blk*.dat files on disk",
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-settings=<file>",
         strprintf(
             "Specify path to dynamic settings data file. Can be disabled with "
             "-nosettings. File is written at runtime and not meant to be "
             "edited by users (use %s instead for custom settings). Relative "
             "paths will be prefixed by datadir location. (default: %s)",
             BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME),
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 #if HAVE_SYSTEM
     argsman.AddArg("-startupnotify=<cmd>", "Execute command on startup.",
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 #endif
 #ifndef WIN32
     argsman.AddArg(
         "-sysperms",
         "Create new files with system default permissions, instead of umask "
         "077 (only effective with disabled wallet functionality)",
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 #else
     hidden_args.emplace_back("-sysperms");
 #endif
     argsman.AddArg("-txindex",
                    strprintf("Maintain a full transaction index, used by the "
                              "getrawtransaction rpc call (default: %d)",
                              DEFAULT_TXINDEX),
                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 #if ENABLE_CHRONIK
     argsman.AddArg(
         "-chronik",
         strprintf("Enable the Chronik indexer, which can be read via a "
                   "dedicated HTTP/Protobuf interface (default: %d)",
                   DEFAULT_CHRONIK),
         ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
     argsman.AddArg(
         "-chronikbind=<addr>[:port]",
         strprintf(
             "Bind the Chronik indexer to the given address to listen for "
             "HTTP/Protobuf connections to access the index. Unlike the "
             "JSON-RPC, it's ok to have this publicly exposed on the internet. "
             "This option can be specified multiple times (default: %s; default "
             "port: %u, testnet: %u, regtest: %u)",
             Join(chronik::DEFAULT_BINDS, ", "),
             defaultBaseParams->ChronikPort(), testnetBaseParams->ChronikPort(),
             regtestBaseParams->ChronikPort()),
         ArgsManager::ALLOW_STRING | ArgsManager::NETWORK_ONLY,
         OptionsCategory::CHRONIK);
     argsman.AddArg("-chroniktokenindex",
                    "Enable token indexing in Chronik (default: 1)",
                    ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
     argsman.AddArg("-chronikreindex",
                    "Reindex the Chronik indexer from genesis, but leave the "
                    "other indexes untouched",
                    ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
     argsman.AddArg(
         "-chroniktxnumcachebuckets",
         strprintf(
             "Tuning param of the TxNumCache, specifies how many buckets "
             "to use on the belt. Caution against setting this too high, "
             "it may slow down indexing. Set to 0 to disable. (default: %d)",
             chronik::DEFAULT_TX_NUM_CACHE_BUCKETS),
         ArgsManager::ALLOW_INT, OptionsCategory::CHRONIK);
     argsman.AddArg(
         "-chroniktxnumcachebucketsize",
         strprintf(
             "Tuning param of the TxNumCache, specifies the size of each bucket "
             "on the belt. Unlike the number of buckets, this may be increased "
             "without much danger of slowing the indexer down. The total cache "
             "size will be `num_buckets * bucket_size * 40B`, so by default the "
             "cache will require %dkB of memory. (default: %d)",
             chronik::DEFAULT_TX_NUM_CACHE_BUCKETS *
                 chronik::DEFAULT_TX_NUM_CACHE_BUCKET_SIZE * 40 / 1000,
             chronik::DEFAULT_TX_NUM_CACHE_BUCKET_SIZE),
         ArgsManager::ALLOW_INT, OptionsCategory::CHRONIK);
     argsman.AddArg("-chronikperfstats",
                    "Output some performance statistics (e.g. num cache hits, "
                    "seconds spent) into a <datadir>/perf folder. (default: 0)",
                    ArgsManager::ALLOW_BOOL, OptionsCategory::CHRONIK);
 #endif
     argsman.AddArg(
         "-blockfilterindex=<type>",
         strprintf("Maintain an index of compact filters by block "
                   "(default: %s, values: %s).",
                   DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
             " If <type> is not supplied or if <type> = 1, indexes for "
             "all known types are enabled.",
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
     argsman.AddArg(
         "-usecashaddr",
         "Use Cash Address for destination encoding instead of base58 "
         "(activate by default on Jan, 14)",
         ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 
     argsman.AddArg(
         "-addnode=<ip>",
         "Add a node to connect to and attempt to keep the connection "
         "open (see the `addnode` RPC command help for more info)",
         ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
         OptionsCategory::CONNECTION);
     argsman.AddArg("-asmap=<file>",
                    strprintf("Specify asn mapping used for bucketing of the "
                              "peers (default: %s). Relative paths will be "
                              "prefixed by the net-specific datadir location.",
                              DEFAULT_ASMAP_FILENAME),
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg("-bantime=<n>",
                    strprintf("Default duration (in seconds) of manually "
                              "configured bans (default: %u)",
                              DEFAULT_MISBEHAVING_BANTIME),
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-bind=<addr>[:<port>][=onion]",
         strprintf("Bind to given address and always listen on it (default: "
                   "0.0.0.0). Use [host]:port notation for IPv6. Append =onion "
                   "to tag any incoming connections to that address and port as "
                   "incoming Tor connections (default: 127.0.0.1:%u=onion, "
                   "testnet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)",
                   defaultBaseParams->OnionServiceTargetPort(),
                   testnetBaseParams->OnionServiceTargetPort(),
                   regtestBaseParams->OnionServiceTargetPort()),
         ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
         OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-connect=<ip>",
         "Connect only to the specified node(s); -connect=0 disables automatic "
         "connections (the rules for this peer are the same as for -addnode)",
         ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
         OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-discover",
         "Discover own IP addresses (default: 1 when listening and no "
         "-externalip or -proxy)",
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg("-dns",
                    strprintf("Allow DNS lookups for -addnode, -seednode and "
                              "-connect (default: %d)",
                              DEFAULT_NAME_LOOKUP),
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-dnsseed",
         strprintf(
             "Query for peer addresses via DNS lookup, if low on addresses "
             "(default: %u unless -connect used)",
             DEFAULT_DNSSEED),
         ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
     argsman.AddArg("-externalip=<ip>", "Specify your own public address",
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-fixedseeds",
         strprintf(
             "Allow fixed seeds if DNS seeds don't provide peers (default: %u)",
             DEFAULT_FIXEDSEEDS),
         ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-forcednsseed",
         strprintf(
             "Always query for peer addresses via DNS lookup (default: %d)",
             DEFAULT_FORCEDNSSEED),
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg("-overridednsseed",
                    "If set, only use the specified DNS seed when "
                    "querying for peer addresses via DNS lookup.",
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-listen",
         "Accept connections from outside (default: 1 if no -proxy or -connect)",
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-listenonion",
         strprintf("Automatically create Tor onion service (default: %d)",
                   DEFAULT_LISTEN_ONION),
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-maxconnections=<n>",
         strprintf("Maintain at most <n> connections to peers. The effective "
                   "limit depends on system limitations and might be lower than "
                   "the specified value (default: %u)",
                   DEFAULT_MAX_PEER_CONNECTIONS),
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg("-maxreceivebuffer=<n>",
                    strprintf("Maximum per-connection receive buffer, <n>*1000 "
                              "bytes (default: %u)",
                              DEFAULT_MAXRECEIVEBUFFER),
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-maxsendbuffer=<n>",
         strprintf(
             "Maximum per-connection send buffer, <n>*1000 bytes (default: %u)",
             DEFAULT_MAXSENDBUFFER),
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-maxtimeadjustment",
         strprintf("Maximum allowed median peer time offset adjustment. Local "
                   "perspective of time may be influenced by peers forward or "
                   "backward by this amount. (default: %u seconds)",
                   DEFAULT_MAX_TIME_ADJUSTMENT),
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg("-onion=<ip:port>",
                    strprintf("Use separate SOCKS5 proxy to reach peers via Tor "
                              "onion services (default: %s)",
                              "-proxy"),
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg("-i2psam=<ip:port>",
                    "I2P SAM proxy to reach I2P peers and accept I2P "
                    "connections (default: none)",
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-i2pacceptincoming",
         "If set and -i2psam is also set then incoming I2P connections are "
         "accepted via the SAM proxy. If this is not set but -i2psam is set "
         "then only outgoing connections will be made to the I2P network. "
         "Ignored if -i2psam is not set. Listening for incoming I2P connections "
         "is done through the SAM proxy, not by binding to a local address and "
         "port (default: 1)",
         ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
 
     argsman.AddArg(
         "-onlynet=<net>",
         "Make outgoing connections only through network <net> (" +
             Join(GetNetworkNames(), ", ") +
             "). Incoming connections are not affected by this option. This "
             "option can be specified multiple times to allow multiple "
             "networks. Warning: if it is used with non-onion networks "
             "and the -onion or -proxy option is set, then outbound onion "
             "connections will still be made; use -noonion or -onion=0 to "
             "disable outbound onion connections in this case",
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg("-peerbloomfilters",
                    strprintf("Support filtering of blocks and transaction with "
                              "bloom filters (default: %d)",
                              DEFAULT_PEERBLOOMFILTERS),
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-peerblockfilters",
         strprintf(
             "Serve compact block filters to peers per BIP 157 (default: %u)",
             DEFAULT_PEERBLOCKFILTERS),
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg("-permitbaremultisig",
                    strprintf("Relay non-P2SH multisig (default: %d)",
                              DEFAULT_PERMIT_BAREMULTISIG),
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     // TODO: remove the sentence "Nodes not using ... incoming connections."
     // once the changes from https://github.com/bitcoin/bitcoin/pull/23542 have
     // become widespread.
     argsman.AddArg("-port=<port>",
                    strprintf("Listen for connections on <port>. Nodes not "
                              "using the default ports (default: %u, "
                              "testnet: %u, regtest: %u) are unlikely to get "
                              "incoming connections.  Not relevant for I2P (see "
                              "doc/i2p.md).",
                              defaultChainParams->GetDefaultPort(),
                              testnetChainParams->GetDefaultPort(),
                              regtestChainParams->GetDefaultPort()),
                    ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
                    OptionsCategory::CONNECTION);
     argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy",
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-proxyrandomize",
         strprintf("Randomize credentials for every proxy connection. "
                   "This enables Tor stream isolation (default: %d)",
                   DEFAULT_PROXYRANDOMIZE),
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-seednode=<ip>",
         "Connect to a node to retrieve peer addresses, and disconnect",
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-networkactive",
         "Enable all P2P network activity (default: 1). Can be changed "
         "by the setnetworkactive RPC command",
         ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
     argsman.AddArg("-timeout=<n>",
                    strprintf("Specify connection timeout in milliseconds "
                              "(minimum: 1, default: %d)",
                              DEFAULT_CONNECT_TIMEOUT),
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-peertimeout=<n>",
         strprintf("Specify p2p connection timeout in seconds. This option "
                   "determines the amount of time a peer may be inactive before "
                   "the connection to it is dropped. (minimum: 1, default: %d)",
                   DEFAULT_PEER_CONNECT_TIMEOUT),
         true, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-torcontrol=<ip>:<port>",
         strprintf(
             "Tor control port to use if onion listening enabled (default: %s)",
             DEFAULT_TOR_CONTROL),
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg("-torpassword=<pass>",
                    "Tor control port password (default: empty)",
                    ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
                    OptionsCategory::CONNECTION);
 #ifdef USE_UPNP
 #if USE_UPNP
     argsman.AddArg("-upnp",
                    "Use UPnP to map the listening port (default: 1 when "
                    "listening and no -proxy)",
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
 #else
     argsman.AddArg(
         "-upnp",
         strprintf("Use UPnP to map the listening port (default: %u)", 0),
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
 #endif
 #else
     hidden_args.emplace_back("-upnp");
 #endif
 #ifdef USE_NATPMP
     argsman.AddArg(
         "-natpmp",
         strprintf("Use NAT-PMP to map the listening port (default: %s)",
                   DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"),
         ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
 #else
     hidden_args.emplace_back("-natpmp");
 #endif // USE_NATPMP
     argsman.AddArg(
         "-whitebind=<[permissions@]addr>",
         "Bind to the given address and add permission flags to the peers "
         "connecting to it."
         "Use [host]:port notation for IPv6. Allowed permissions: " +
             Join(NET_PERMISSIONS_DOC, ", ") +
             ". "
             "Specify multiple permissions separated by commas (default: "
             "download,noban,mempool,relay). Can be specified multiple times.",
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
 
     argsman.AddArg("-whitelist=<[permissions@]IP address or network>",
                    "Add permission flags to the peers connecting from the "
                    "given IP address (e.g. 1.2.3.4) or CIDR-notated network "
                    "(e.g. 1.2.3.0/24). "
                    "Uses the same permissions as -whitebind. Can be specified "
                    "multiple times.",
                    ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
     argsman.AddArg(
         "-maxuploadtarget=<n>",
         strprintf("Tries to keep outbound traffic under the given target (in "
                   "MiB per 24h). Limit does not apply to peers with 'download' "
                   "permission. 0 = no limit (default: %d)",
                   DEFAULT_MAX_UPLOAD_TARGET),
         ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
 
     g_wallet_init_interface.AddWalletOptions(argsman);
 
 #if ENABLE_ZMQ
     argsman.AddArg("-zmqpubhashblock=<address>",
                    "Enable publish hash block in <address>",
                    ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
     argsman.AddArg("-zmqpubhashtx=<address>",
                    "Enable publish hash transaction in <address>",
                    ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
     argsman.AddArg("-zmqpubrawblock=<address>",
                    "Enable publish raw block in <address>",
                    ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
     argsman.AddArg("-zmqpubrawtx=<address>",
                    "Enable publish raw transaction in <address>",
                    ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
     argsman.AddArg("-zmqpubsequence=<address>",
                    "Enable publish hash block and tx sequence in <address>",
                    ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
     argsman.AddArg(
         "-zmqpubhashblockhwm=<n>",
         strprintf("Set publish hash block outbound message high water "
                   "mark (default: %d)",
                   CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
         ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
     argsman.AddArg(
         "-zmqpubhashtxhwm=<n>",
         strprintf("Set publish hash transaction outbound message high "
                   "water mark (default: %d)",
                   CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
         false, OptionsCategory::ZMQ);
     argsman.AddArg(
         "-zmqpubrawblockhwm=<n>",
         strprintf("Set publish raw block outbound message high water "
                   "mark (default: %d)",
                   CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
         ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
     argsman.AddArg(
         "-zmqpubrawtxhwm=<n>",
         strprintf("Set publish raw transaction outbound message high "
                   "water mark (default: %d)",
                   CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
         ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
     argsman.AddArg("-zmqpubsequencehwm=<n>",
                    strprintf("Set publish hash sequence message high water mark"
                              " (default: %d)",
                              CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM),
                    ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
 #else
     hidden_args.emplace_back("-zmqpubhashblock=<address>");
     hidden_args.emplace_back("-zmqpubhashtx=<address>");
     hidden_args.emplace_back("-zmqpubrawblock=<address>");
     hidden_args.emplace_back("-zmqpubrawtx=<address>");
     hidden_args.emplace_back("-zmqpubsequence=<n>");
     hidden_args.emplace_back("-zmqpubhashblockhwm=<n>");
     hidden_args.emplace_back("-zmqpubhashtxhwm=<n>");
     hidden_args.emplace_back("-zmqpubrawblockhwm=<n>");
     hidden_args.emplace_back("-zmqpubrawtxhwm=<n>");
     hidden_args.emplace_back("-zmqpubsequencehwm=<n>");
 #endif
 
     argsman.AddArg(
         "-checkblocks=<n>",
         strprintf("How many blocks to check at startup (default: %u, 0 = all)",
                   DEFAULT_CHECKBLOCKS),
         ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
         OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-checklevel=<n>",
                    strprintf("How thorough the block verification of "
                              "-checkblocks is: %s (0-4, default: %u)",
                              Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL),
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-checkblockindex",
                    strprintf("Do a consistency check for the block tree, "
                              "chainstate, and other validation data structures "
                              "occasionally. (default: %u, regtest: %u)",
                              defaultChainParams->DefaultConsistencyChecks(),
                              regtestChainParams->DefaultConsistencyChecks()),
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-checkaddrman=<n>",
                    strprintf("Run addrman consistency checks every <n> "
                              "operations. Use 0 to disable. (default: %u)",
                              DEFAULT_ADDRMAN_CONSISTENCY_CHECKS),
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
     argsman.AddArg(
         "-checkmempool=<n>",
         strprintf("Run mempool consistency checks every <n> transactions. Use "
                   "0 to disable. (default: %u, regtest: %u)",
                   defaultChainParams->DefaultConsistencyChecks(),
                   regtestChainParams->DefaultConsistencyChecks()),
         ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
         OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-checkpoints",
                    strprintf("Only accept block chain matching built-in "
                              "checkpoints (default: %d)",
                              DEFAULT_CHECKPOINTS_ENABLED),
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-deprecatedrpc=<method>",
                    "Allows deprecated RPC method(s) to be used",
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
     argsman.AddArg(
         "-stopafterblockimport",
         strprintf("Stop running after importing blocks from disk (default: %d)",
                   DEFAULT_STOPAFTERBLOCKIMPORT),
         ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
         OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-stopatheight",
                    strprintf("Stop running after reaching the given height in "
                              "the main chain (default: %u)",
                              DEFAULT_STOPATHEIGHT),
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-addrmantest", "Allows to test address relay on localhost",
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-capturemessages", "Capture all P2P messages to disk",
                    ArgsManager::ALLOW_BOOL | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-mocktime=<n>",
                    "Replace actual time with " + UNIX_EPOCH_TIME +
                        " (default: 0)",
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
     argsman.AddArg(
         "-maxsigcachesize=<n>",
         strprintf("Limit size of signature cache to <n> MiB (default: %u)",
                   DEFAULT_MAX_SIG_CACHE_BYTES >> 20),
         ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
         OptionsCategory::DEBUG_TEST);
     argsman.AddArg(
         "-maxscriptcachesize=<n>",
         strprintf("Limit size of script cache to <n> MiB (default: %u)",
                   DEFAULT_MAX_SCRIPT_CACHE_BYTES >> 20),
         ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
         OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-maxtipage=<n>",
                    strprintf("Maximum tip age in seconds to consider node in "
                              "initial block download (default: %u)",
                              Ticks<std::chrono::seconds>(DEFAULT_MAX_TIP_AGE)),
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::DEBUG_TEST);
 
     argsman.AddArg("-uacomment=<cmt>",
                    "Append comment to the user agent string",
                    ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-uaclientname=<clientname>", "Set user agent client name",
                    ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
     argsman.AddArg("-uaclientversion=<clientversion>",
                    "Set user agent client version", ArgsManager::ALLOW_ANY,
                    OptionsCategory::DEBUG_TEST);
 
     SetupChainParamsBaseOptions(argsman);
 
     argsman.AddArg(
         "-acceptnonstdtxn",
         strprintf(
             "Relay and mine \"non-standard\" transactions (%sdefault: %u)",
             "testnet/regtest only; ", defaultChainParams->RequireStandard()),
         ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
         OptionsCategory::NODE_RELAY);
     argsman.AddArg("-excessiveblocksize=<n>",
                    strprintf("Do not accept blocks larger than this limit, in "
                              "bytes (default: %d)",
                              DEFAULT_MAX_BLOCK_SIZE),
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::NODE_RELAY);
     const auto &ticker = Currency::get().ticker;
     argsman.AddArg(
         "-dustrelayfee=<amt>",
         strprintf("Fee rate (in %s/kB) used to define dust, the value of an "
                   "output such that it will cost about 1/3 of its value in "
                   "fees at this fee rate to spend it. (default: %s)",
                   ticker, FormatMoney(DUST_RELAY_TX_FEE)),
         ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
         OptionsCategory::NODE_RELAY);
 
     argsman.AddArg(
         "-bytespersigcheck",
         strprintf("Equivalent bytes per sigCheck in transactions for relay and "
                   "mining (default: %u).",
                   DEFAULT_BYTES_PER_SIGCHECK),
         ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
     argsman.AddArg(
         "-bytespersigop",
         strprintf("DEPRECATED: Equivalent bytes per sigCheck in transactions "
                   "for relay and mining (default: %u). This has been "
                   "deprecated since v0.26.8 and will be removed in the future, "
                   "please use -bytespersigcheck instead.",
                   DEFAULT_BYTES_PER_SIGCHECK),
         ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
     argsman.AddArg(
         "-datacarrier",
         strprintf("Relay and mine data carrier transactions (default: %d)",
                   DEFAULT_ACCEPT_DATACARRIER),
         ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
     argsman.AddArg(
         "-datacarriersize",
         strprintf("Maximum size of data in data carrier transactions "
                   "we relay and mine (default: %u)",
                   MAX_OP_RETURN_RELAY),
         ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
     argsman.AddArg(
         "-minrelaytxfee=<amt>",
         strprintf("Fees (in %s/kB) smaller than this are rejected for "
                   "relaying, mining and transaction creation (default: %s)",
                   ticker, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE_PER_KB)),
         ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
     argsman.AddArg(
         "-whitelistrelay",
         strprintf("Add 'relay' permission to whitelisted inbound peers "
                   "with default permissions. This will accept relayed "
                   "transactions even when not relaying transactions "
                   "(default: %d)",
                   DEFAULT_WHITELISTRELAY),
         ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
     argsman.AddArg(
         "-whitelistforcerelay",
         strprintf("Add 'forcerelay' permission to whitelisted inbound peers"
                   " with default permissions. This will relay transactions "
                   "even if the transactions were already in the mempool "
                   "(default: %d)",
                   DEFAULT_WHITELISTFORCERELAY),
         ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
 
     argsman.AddArg("-blockmaxsize=<n>",
                    strprintf("Set maximum block size in bytes (default: %d)",
                              DEFAULT_MAX_GENERATED_BLOCK_SIZE),
                    ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
     argsman.AddArg(
         "-blockmintxfee=<amt>",
         strprintf("Set lowest fee rate (in %s/kB) for transactions to "
                   "be included in block creation. (default: %s)",
                   ticker, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB)),
         ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
 
     argsman.AddArg("-blockversion=<n>",
                    "Override block version to test forking scenarios",
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::BLOCK_CREATION);
 
     argsman.AddArg("-server", "Accept command line and JSON-RPC commands",
                    ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
     argsman.AddArg("-rest",
                    strprintf("Accept public REST requests (default: %d)",
                              DEFAULT_REST_ENABLE),
                    ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
     argsman.AddArg(
         "-rpcbind=<addr>[:port]",
         "Bind to given address to listen for JSON-RPC connections. Do not "
         "expose the RPC server to untrusted networks such as the public "
         "internet! This option is ignored unless -rpcallowip is also passed. "
         "Port is optional and overrides -rpcport.  Use [host]:port notation "
         "for IPv6. This option can be specified multiple times (default: "
         "127.0.0.1 and ::1 i.e., localhost)",
         ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY |
             ArgsManager::SENSITIVE,
         OptionsCategory::RPC);
     argsman.AddArg(
         "-rpccookiefile=<loc>",
         "Location of the auth cookie. Relative paths will be prefixed "
         "by a net-specific datadir location. (default: data dir)",
         ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
     argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections",
                    ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
                    OptionsCategory::RPC);
     argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections",
                    ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
                    OptionsCategory::RPC);
     argsman.AddArg(
         "-rpcwhitelist=<whitelist>",
         "Set a whitelist to filter incoming RPC calls for a specific user. The "
         "field <whitelist> comes in the format: <USERNAME>:<rpc 1>,<rpc "
         "2>,...,<rpc n>. If multiple whitelists are set for a given user, they "
         "are set-intersected. See -rpcwhitelistdefault documentation for "
         "information on default whitelist behavior.",
         ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
     argsman.AddArg(
         "-rpcwhitelistdefault",
         "Sets default behavior for rpc whitelisting. Unless "
         "rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc "
         "server acts as if all rpc users are subject to "
         "empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault "
         "is set to 1 and no -rpcwhitelist is set, rpc server acts as if all "
         "rpc users are subject to empty whitelists.",
         ArgsManager::ALLOW_BOOL, OptionsCategory::RPC);
     argsman.AddArg(
         "-rpcauth=<userpw>",
         "Username and HMAC-SHA-256 hashed password for JSON-RPC connections. "
         "The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A "
         "canonical python script is included in share/rpcauth. The client then "
         "connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> "
         "pair of arguments. This option can be specified multiple times",
         ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
     argsman.AddArg("-rpcport=<port>",
                    strprintf("Listen for JSON-RPC connections on <port> "
                              "(default: %u, testnet: %u, regtest: %u)",
                              defaultBaseParams->RPCPort(),
                              testnetBaseParams->RPCPort(),
                              regtestBaseParams->RPCPort()),
                    ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
                    OptionsCategory::RPC);
     argsman.AddArg(
         "-rpcallowip=<ip>",
         "Allow JSON-RPC connections from specified source. Valid for "
         "<ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. "
         "1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). "
         "This option can be specified multiple times",
         ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
     argsman.AddArg(
         "-rpcthreads=<n>",
         strprintf(
             "Set the number of threads to service RPC calls (default: %d)",
             DEFAULT_HTTP_THREADS),
         ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
     argsman.AddArg(
         "-rpccorsdomain=value",
         "Domain from which to accept cross origin requests (browser enforced)",
         ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
 
     argsman.AddArg("-rpcworkqueue=<n>",
                    strprintf("Set the depth of the work queue to service RPC "
                              "calls (default: %d)",
                              DEFAULT_HTTP_WORKQUEUE),
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::RPC);
     argsman.AddArg("-rpcservertimeout=<n>",
                    strprintf("Timeout during HTTP requests (default: %d)",
                              DEFAULT_HTTP_SERVER_TIMEOUT),
                    ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
                    OptionsCategory::RPC);
 
 #if HAVE_DECL_FORK
     argsman.AddArg("-daemon",
                    strprintf("Run in the background as a daemon and accept "
                              "commands (default: %d)",
                              DEFAULT_DAEMON),
                    ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
     argsman.AddArg("-daemonwait",
                    strprintf("Wait for initialization to be finished before "
                              "exiting. This implies -daemon (default: %d)",
                              DEFAULT_DAEMONWAIT),
                    ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
 #else
     hidden_args.emplace_back("-daemon");
     hidden_args.emplace_back("-daemonwait");
 #endif
 
     // Avalanche options.
     argsman.AddArg("-avalanche",
                    strprintf("Enable the avalanche feature (default: %u)",
                              AVALANCHE_DEFAULT_ENABLED),
                    ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-avalanchestakingrewards",
         strprintf("Enable the avalanche staking rewards feature (default: %u, "
                   "testnet: %u, regtest: %u)",
                   defaultChainParams->GetConsensus().enableStakingRewards,
                   testnetChainParams->GetConsensus().enableStakingRewards,
                   regtestChainParams->GetConsensus().enableStakingRewards),
         ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE);
     argsman.AddArg("-avalancheconflictingproofcooldown",
                    strprintf("Mandatory cooldown before a proof conflicting "
                              "with an already registered one can be considered "
                              "in seconds (default: %u)",
                              AVALANCHE_DEFAULT_CONFLICTING_PROOF_COOLDOWN),
                    ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
     argsman.AddArg("-avalanchepeerreplacementcooldown",
                    strprintf("Mandatory cooldown before a peer can be replaced "
                              "in seconds (default: %u)",
                              AVALANCHE_DEFAULT_PEER_REPLACEMENT_COOLDOWN),
                    ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-avaminquorumstake",
         strprintf(
             "Minimum amount of known stake for a usable quorum (default: %s)",
             FormatMoney(AVALANCHE_DEFAULT_MIN_QUORUM_STAKE)),
         ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-avaminquorumconnectedstakeratio",
         strprintf("Minimum proportion of known stake we"
                   " need nodes for to have a usable quorum (default: %s)",
                   AVALANCHE_DEFAULT_MIN_QUORUM_CONNECTED_STAKE_RATIO),
         ArgsManager::ALLOW_STRING, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-avaminavaproofsnodecount",
         strprintf("Minimum number of node that needs to send us an avaproofs"
                   " message before we consider we have a usable quorum"
                   " (default: %s)",
                   AVALANCHE_DEFAULT_MIN_AVAPROOFS_NODE_COUNT),
         ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-avastalevotethreshold",
         strprintf("Number of avalanche votes before a voted item goes stale "
                   "when voting confidence is low (default: %u)",
                   AVALANCHE_VOTE_STALE_THRESHOLD),
         ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-avastalevotefactor",
         strprintf(
             "Factor affecting the number of avalanche votes before a voted "
             "item goes stale when voting confidence is high (default: %u)",
             AVALANCHE_VOTE_STALE_FACTOR),
         ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
     argsman.AddArg("-avacooldown",
                    strprintf("Mandatory cooldown between two avapoll in "
                              "milliseconds (default: %u)",
                              AVALANCHE_DEFAULT_COOLDOWN),
                    ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-avatimeout",
         strprintf("Avalanche query timeout in milliseconds (default: %u)",
                   AVALANCHE_DEFAULT_QUERY_TIMEOUT.count()),
         ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-avadelegation",
         "Avalanche proof delegation to the master key used by this node "
         "(default: none). Should be used in conjunction with -avaproof and "
         "-avamasterkey",
         ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
     argsman.AddArg("-avaproof",
                    "Avalanche proof to be used by this node (default: none)",
                    ArgsManager::ALLOW_ANY, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-avaproofstakeutxoconfirmations",
         strprintf(
             "Minimum number of confirmations before a stake utxo is mature"
             " enough to be included into a proof. Utxos in the mempool are not "
             "accepted (i.e this value must be greater than 0) (default: %s)",
             AVALANCHE_DEFAULT_STAKE_UTXO_CONFIRMATIONS),
         ArgsManager::ALLOW_INT, OptionsCategory::HIDDEN);
     argsman.AddArg("-avaproofstakeutxodustthreshold",
                    strprintf("Minimum value each stake utxo must have to be "
                              "considered valid (default: %s)",
                              avalanche::PROOF_DUST_THRESHOLD),
                    ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
     argsman.AddArg("-avamasterkey",
                    "Master key associated with the proof. If a proof is "
                    "required, this is mandatory.",
                    ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
                    OptionsCategory::AVALANCHE);
     argsman.AddArg("-avasessionkey", "Avalanche session key (default: random)",
                    ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE,
                    OptionsCategory::HIDDEN);
     argsman.AddArg(
         "-maxavalancheoutbound",
         strprintf(
             "Set the maximum number of avalanche outbound peers to connect to. "
             "Note that this option takes precedence over the -maxconnections "
             "option (default: %u).",
             DEFAULT_MAX_AVALANCHE_OUTBOUND_CONNECTIONS),
         ArgsManager::ALLOW_INT, OptionsCategory::AVALANCHE);
     argsman.AddArg(
         "-persistavapeers",
         strprintf("Whether to save the avalanche peers upon shutdown and load "
                   "them upon startup (default: %u).",
                   DEFAULT_PERSIST_AVAPEERS),
         ArgsManager::ALLOW_BOOL, OptionsCategory::AVALANCHE);
 
     hidden_args.emplace_back("-avalanchepreconsensus");
 
     // Add the hidden options
     argsman.AddHiddenArgs(hidden_args);
 }
 
 std::string LicenseInfo() {
     const std::string URL_SOURCE_CODE =
         "<https://github.com/Bitcoin-ABC/bitcoin-abc>";
     const std::string URL_WEBSITE = "<https://www.bitcoinabc.org>";
 
     return CopyrightHolders(strprintf(_("Copyright (C) %i-%i").translated, 2009,
                                       COPYRIGHT_YEAR) +
                             " ") +
            "\n" + "\n" +
            strprintf(_("Please contribute if you find %s useful. "
                        "Visit %s for further information about the software.")
                          .translated,
                      PACKAGE_NAME, URL_WEBSITE) +
            "\n" +
            strprintf(_("The source code is available from %s.").translated,
                      URL_SOURCE_CODE) +
            "\n" + "\n" + _("This is experimental software.").translated + "\n" +
            strprintf(_("Distributed under the MIT software license, see the "
                        "accompanying file %s or %s")
                          .translated,
                      "COPYING", "<https://opensource.org/licenses/MIT>") +
            "\n" + "\n" +
            strprintf(_("This product includes software developed by the "
                        "OpenSSL Project for use in the OpenSSL Toolkit %s and "
                        "cryptographic software written by Eric Young and UPnP "
                        "software written by Thomas Bernard.")
                          .translated,
                      "<https://www.openssl.org>") +
            "\n";
 }
 
 static bool fHaveGenesis = false;
 static GlobalMutex g_genesis_wait_mutex;
 static std::condition_variable g_genesis_wait_cv;
 
 static void BlockNotifyGenesisWait(const CBlockIndex *pBlockIndex) {
     if (pBlockIndex != nullptr) {
         {
             LOCK(g_genesis_wait_mutex);
             fHaveGenesis = true;
         }
         g_genesis_wait_cv.notify_all();
     }
 }
 
 #if HAVE_SYSTEM
 static void StartupNotify(const ArgsManager &args) {
     std::string cmd = args.GetArg("-startupnotify", "");
     if (!cmd.empty()) {
         std::thread t(runCommand, cmd);
         // thread runs free
         t.detach();
     }
 }
 #endif
 
 static bool AppInitServers(Config &config,
                            HTTPRPCRequestProcessor &httpRPCRequestProcessor,
                            NodeContext &node) {
     const ArgsManager &args = *Assert(node.args);
     RPCServerSignals::OnStarted(&OnRPCStarted);
     RPCServerSignals::OnStopped(&OnRPCStopped);
     if (!InitHTTPServer(config)) {
         return false;
     }
 
     StartRPC();
     node.rpc_interruption_point = RpcInterruptionPoint;
 
     if (!StartHTTPRPC(httpRPCRequestProcessor)) {
         return false;
     }
     if (args.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) {
         StartREST(&node);
     }
 
     StartHTTPServer();
     return true;
 }
 
 // Parameter interaction based on rules
 void InitParameterInteraction(ArgsManager &args) {
     // when specifying an explicit binding address, you want to listen on it
     // even when -connect or -proxy is specified.
     if (args.IsArgSet("-bind")) {
         if (args.SoftSetBoolArg("-listen", true)) {
             LogPrintf(
                 "%s: parameter interaction: -bind set -> setting -listen=1\n",
                 __func__);
         }
     }
     if (args.IsArgSet("-whitebind")) {
         if (args.SoftSetBoolArg("-listen", true)) {
             LogPrintf("%s: parameter interaction: -whitebind set -> setting "
                       "-listen=1\n",
                       __func__);
         }
     }
 
     if (args.IsArgSet("-connect")) {
         // when only connecting to trusted nodes, do not seed via DNS, or listen
         // by default.
         if (args.SoftSetBoolArg("-dnsseed", false)) {
             LogPrintf("%s: parameter interaction: -connect set -> setting "
                       "-dnsseed=0\n",
                       __func__);
         }
         if (args.SoftSetBoolArg("-listen", false)) {
             LogPrintf("%s: parameter interaction: -connect set -> setting "
                       "-listen=0\n",
                       __func__);
         }
     }
 
     if (args.IsArgSet("-proxy")) {
         // to protect privacy, do not listen by default if a default proxy
         // server is specified.
         if (args.SoftSetBoolArg("-listen", false)) {
             LogPrintf(
                 "%s: parameter interaction: -proxy set -> setting -listen=0\n",
                 __func__);
         }
         // to protect privacy, do not map ports when a proxy is set. The user
         // may still specify -listen=1 to listen locally, so don't rely on this
         // happening through -listen below.
         if (args.SoftSetBoolArg("-upnp", false)) {
             LogPrintf(
                 "%s: parameter interaction: -proxy set -> setting -upnp=0\n",
                 __func__);
         }
         if (args.SoftSetBoolArg("-natpmp", false)) {
             LogPrintf(
                 "%s: parameter interaction: -proxy set -> setting -natpmp=0\n",
                 __func__);
         }
         // to protect privacy, do not discover addresses by default
         if (args.SoftSetBoolArg("-discover", false)) {
             LogPrintf("%s: parameter interaction: -proxy set -> setting "
                       "-discover=0\n",
                       __func__);
         }
     }
 
     if (!args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
         // do not map ports or try to retrieve public IP when not listening
         // (pointless)
         if (args.SoftSetBoolArg("-upnp", false)) {
             LogPrintf(
                 "%s: parameter interaction: -listen=0 -> setting -upnp=0\n",
                 __func__);
         }
         if (args.SoftSetBoolArg("-natpmp", false)) {
             LogPrintf(
                 "%s: parameter interaction: -listen=0 -> setting -natpmp=0\n",
                 __func__);
         }
         if (args.SoftSetBoolArg("-discover", false)) {
             LogPrintf(
                 "%s: parameter interaction: -listen=0 -> setting -discover=0\n",
                 __func__);
         }
         if (args.SoftSetBoolArg("-listenonion", false)) {
             LogPrintf("%s: parameter interaction: -listen=0 -> setting "
                       "-listenonion=0\n",
                       __func__);
         }
         if (args.SoftSetBoolArg("-i2pacceptincoming", false)) {
             LogPrintf("%s: parameter interaction: -listen=0 -> setting "
                       "-i2pacceptincoming=0\n",
                       __func__);
         }
     }
 
     if (args.IsArgSet("-externalip")) {
         // if an explicit public IP is specified, do not try to find others
         if (args.SoftSetBoolArg("-discover", false)) {
             LogPrintf("%s: parameter interaction: -externalip set -> setting "
                       "-discover=0\n",
                       __func__);
         }
     }
 
     // disable whitelistrelay in blocksonly mode
     if (args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
         if (args.SoftSetBoolArg("-whitelistrelay", false)) {
             LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting "
                       "-whitelistrelay=0\n",
                       __func__);
         }
     }
 
     // Forcing relay from whitelisted hosts implies we will accept relays from
     // them in the first place.
     if (args.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
         if (args.SoftSetBoolArg("-whitelistrelay", true)) {
             LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> "
                       "setting -whitelistrelay=1\n",
                       __func__);
         }
     }
 
     // If avalanche is set, soft set all the feature flags accordingly.
     if (args.IsArgSet("-avalanche")) {
         const bool fAvalanche =
             args.GetBoolArg("-avalanche", AVALANCHE_DEFAULT_ENABLED);
         args.SoftSetBoolArg("-automaticunparking", !fAvalanche);
     }
 }
 
 /**
  * Initialize global loggers.
  *
  * Note that this is called very early in the process lifetime, so you should be
  * careful about what global state you rely on here.
  */
 void InitLogging(const ArgsManager &args) {
     init::SetLoggingOptions(args);
     init::LogPackageVersion();
 }
 
 namespace { // Variables internal to initialization process only
 
 int nMaxConnections;
 int nUserMaxConnections;
 int nFD;
 ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
 int64_t peer_connect_timeout;
 std::set<BlockFilterType> g_enabled_filter_types;
 
 } // namespace
 
 [[noreturn]] static void new_handler_terminate() {
     // Rather than throwing std::bad-alloc if allocation fails, terminate
     // immediately to (try to) avoid chain corruption. Since LogPrintf may
     // itself allocate memory, set the handler directly to terminate first.
     std::set_new_handler(std::terminate);
     LogPrintf("Error: Out of memory. Terminating.\n");
 
     // The log was successful, terminate now.
     std::terminate();
 };
 
 bool AppInitBasicSetup(const ArgsManager &args) {
 // Step 1: setup
 #ifdef _MSC_VER
     // Turn off Microsoft heap dump noise
     _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
     _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr,
                                              OPEN_EXISTING, 0, 0));
     // Disable confusing "helpful" text message on abort, Ctrl-C
     _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
 #endif
 #ifdef WIN32
     // Enable Data Execution Prevention (DEP)
     SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
 #endif
     if (!InitShutdownState()) {
         return InitError(
             Untranslated("Initializing wait-for-shutdown state failed."));
     }
 
     if (!SetupNetworking()) {
         return InitError(Untranslated("Initializing networking failed"));
     }
 
 #ifndef WIN32
     if (!args.GetBoolArg("-sysperms", false)) {
         umask(077);
     }
 
     // Clean shutdown on SIGTERM
     registerSignalHandler(SIGTERM, HandleSIGTERM);
     registerSignalHandler(SIGINT, HandleSIGTERM);
 
     // Reopen debug.log on SIGHUP
     registerSignalHandler(SIGHUP, HandleSIGHUP);
 
     // Ignore SIGPIPE, otherwise it will bring the daemon down if the client
     // closes unexpectedly
     signal(SIGPIPE, SIG_IGN);
 #else
     SetConsoleCtrlHandler(consoleCtrlHandler, true);
 #endif
 
     std::set_new_handler(new_handler_terminate);
 
     return true;
 }
 
 bool AppInitParameterInteraction(Config &config, const ArgsManager &args) {
     const CChainParams &chainparams = config.GetChainParams();
     // Step 2: parameter interactions
 
     // also see: InitParameterInteraction()
 
     // Error if network-specific options (-addnode, -connect, etc) are
     // specified in default section of config file, but not overridden
     // on the command line or in this network's section of the config file.
     std::string network = args.GetChainName();
     bilingual_str errors;
     for (const auto &arg : args.GetUnsuitableSectionOnlyArgs()) {
         errors += strprintf(_("Config setting for %s only applied on %s "
                               "network when in [%s] section.") +
                                 Untranslated("\n"),
                             arg, network, network);
     }
 
     if (!errors.empty()) {
         return InitError(errors);
     }
 
     // Warn if unrecognized section name are present in the config file.
     bilingual_str warnings;
     for (const auto &section : args.GetUnrecognizedSections()) {
         warnings += strprintf(Untranslated("%s:%i ") +
                                   _("Section [%s] is not recognized.") +
                                   Untranslated("\n"),
                               section.m_file, section.m_line, section.m_name);
     }
 
     if (!warnings.empty()) {
         InitWarning(warnings);
     }
 
     if (!fs::is_directory(args.GetBlocksDirPath())) {
         return InitError(
             strprintf(_("Specified blocks directory \"%s\" does not exist."),
                       args.GetArg("-blocksdir", "")));
     }
 
     // parse and validate enabled filter types
     std::string blockfilterindex_value =
         args.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
     if (blockfilterindex_value == "" || blockfilterindex_value == "1") {
         g_enabled_filter_types = AllBlockFilterTypes();
     } else if (blockfilterindex_value != "0") {
         const std::vector<std::string> names =
             args.GetArgs("-blockfilterindex");
         for (const auto &name : names) {
             BlockFilterType filter_type;
             if (!BlockFilterTypeByName(name, filter_type)) {
                 return InitError(
                     strprintf(_("Unknown -blockfilterindex value %s."), name));
             }
             g_enabled_filter_types.insert(filter_type);
         }
     }
 
     // Signal NODE_COMPACT_FILTERS if peerblockfilters and basic filters index
     // are both enabled.
     if (args.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)) {
         if (g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) {
             return InitError(
                 _("Cannot set -peerblockfilters without -blockfilterindex."));
         }
 
         nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS);
     }
 
     // if using block pruning, then disallow txindex, coinstatsindex and chronik
     if (args.GetIntArg("-prune", 0)) {
         if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
             return InitError(_("Prune mode is incompatible with -txindex."));
         }
         if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
             return InitError(
                 _("Prune mode is incompatible with -coinstatsindex."));
         }
         if (args.GetBoolArg("-chronik", DEFAULT_CHRONIK)) {
             return InitError(_("Prune mode is incompatible with -chronik."));
         }
     }
 
     // -bind and -whitebind can't be set when not listening
     size_t nUserBind =
         args.GetArgs("-bind").size() + args.GetArgs("-whitebind").size();
     if (nUserBind != 0 && !args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
         return InitError(Untranslated(
             "Cannot set -bind or -whitebind together with -listen=0"));
     }
 
     // Make sure enough file descriptors are available
     int nBind = std::max(nUserBind, size_t(1));
     nUserMaxConnections =
         args.GetIntArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
     nMaxConnections = std::max(nUserMaxConnections, 0);
 
     // -maxavalancheoutbound takes precedence over -maxconnections
     const int maxAvalancheOutbound = args.GetIntArg(
         "-maxavalancheoutbound", DEFAULT_MAX_AVALANCHE_OUTBOUND_CONNECTIONS);
     if (isAvalancheEnabled(args) && maxAvalancheOutbound > nMaxConnections) {
         nMaxConnections = std::max(maxAvalancheOutbound, nMaxConnections);
         // Indicate the value set by the user
         LogPrintf("Increasing -maxconnections from %d to %d to comply with "
                   "-maxavalancheoutbound\n",
                   nUserMaxConnections, nMaxConnections);
     }
 
     // Trim requested connection counts, to fit into system limitations
     // <int> in std::min<int>(...) to work around FreeBSD compilation issue
     // described in #2695
     nFD = RaiseFileDescriptorLimit(
         nMaxConnections + nBind + MIN_CORE_FILEDESCRIPTORS +
         MAX_ADDNODE_CONNECTIONS + NUM_FDS_MESSAGE_CAPTURE);
 #ifdef USE_POLL
     int fd_max = nFD;
 #else
     int fd_max = FD_SETSIZE;
 #endif
     nMaxConnections = std::max(
         std::min<int>(nMaxConnections,
                       fd_max - nBind - MIN_CORE_FILEDESCRIPTORS -
                           MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE),
         0);
     if (nFD < MIN_CORE_FILEDESCRIPTORS) {
         return InitError(_("Not enough file descriptors available."));
     }
     nMaxConnections =
         std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS,
                  nMaxConnections);
 
     if (nMaxConnections < nUserMaxConnections) {
         // Not categorizing as "Warning" because this is the normal behavior for
         // platforms using the select() interface for which FD_SETSIZE is
         // usually 1024.
         LogPrintf("Reducing -maxconnections from %d to %d, because of system "
                   "limitations.\n",
                   nUserMaxConnections, nMaxConnections);
     }
 
     // Step 3: parameter-to-internal-flags
     init::SetLoggingCategories(args);
 
     // Configure excessive block size.
     const int64_t nProposedExcessiveBlockSize =
         args.GetIntArg("-excessiveblocksize", DEFAULT_MAX_BLOCK_SIZE);
     if (nProposedExcessiveBlockSize <= 0 ||
         !config.SetMaxBlockSize(nProposedExcessiveBlockSize)) {
         return InitError(
             _("Excessive block size must be > 1,000,000 bytes (1MB)"));
     }
 
     // Check blockmaxsize does not exceed maximum accepted block size.
     const int64_t nProposedMaxGeneratedBlockSize =
         args.GetIntArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE);
     if (nProposedMaxGeneratedBlockSize <= 0) {
         return InitError(_("Max generated block size must be greater than 0"));
     }
     if (uint64_t(nProposedMaxGeneratedBlockSize) > config.GetMaxBlockSize()) {
         return InitError(_("Max generated block size (blockmaxsize) cannot "
                            "exceed the excessive block size "
                            "(excessiveblocksize)"));
     }
 
     // block pruning; get the amount of disk space (in MiB) to allot for block &
     // undo files
     int64_t nPruneArg = args.GetIntArg("-prune", 0);
     if (nPruneArg < 0) {
         return InitError(
             _("Prune cannot be configured with a negative value."));
     }
     nPruneTarget = (uint64_t)nPruneArg * 1024 * 1024;
     if (nPruneArg == 1) {
         // manual pruning: -prune=1
         nPruneTarget = std::numeric_limits<uint64_t>::max();
         fPruneMode = true;
     } else if (nPruneTarget) {
         if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) {
             return InitError(
                 strprintf(_("Prune configured below the minimum of %d MiB. "
                             "Please use a higher number."),
                           MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
         }
         fPruneMode = true;
     }
 
     nConnectTimeout = args.GetIntArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
     if (nConnectTimeout <= 0) {
         nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
     }
 
     peer_connect_timeout =
         args.GetIntArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
     if (peer_connect_timeout <= 0) {
         return InitError(Untranslated(
             "peertimeout cannot be configured with a negative value."));
     }
 
     // Sanity check argument for min fee for including tx in block
     // TODO: Harmonize which arguments need sanity checking and where that
     // happens.
     if (args.IsArgSet("-blockmintxfee")) {
         Amount n = Amount::zero();
         if (!ParseMoney(args.GetArg("-blockmintxfee", ""), n)) {
             return InitError(AmountErrMsg("blockmintxfee",
                                           args.GetArg("-blockmintxfee", "")));
         }
     }
 
     nBytesPerSigCheck =
         args.IsArgSet("-bytespersigcheck")
             ? args.GetIntArg("-bytespersigcheck", nBytesPerSigCheck)
             : args.GetIntArg("-bytespersigop", nBytesPerSigCheck);
 
     if (!g_wallet_init_interface.ParameterInteraction()) {
         return false;
     }
 
     // Option to startup with mocktime set (used for regression testing):
     SetMockTime(args.GetIntArg("-mocktime", 0)); // SetMockTime(0) is a no-op
 
     if (args.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) {
         nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
     }
 
     if (args.IsArgSet("-proxy") && args.GetArg("-proxy", "").empty()) {
         return InitError(_(
             "No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
     }
 
     // Avalanche parameters
     const int64_t stakeUtxoMinConfirmations =
         args.GetIntArg("-avaproofstakeutxoconfirmations",
                        AVALANCHE_DEFAULT_STAKE_UTXO_CONFIRMATIONS);
 
     if (!chainparams.IsTestChain() &&
         stakeUtxoMinConfirmations !=
             AVALANCHE_DEFAULT_STAKE_UTXO_CONFIRMATIONS) {
         return InitError(_("Avalanche stake UTXO minimum confirmations can "
                            "only be set on test chains."));
     }
 
     if (stakeUtxoMinConfirmations <= 0) {
         return InitError(_("Avalanche stake UTXO minimum confirmations must be "
                            "a positive integer."));
     }
 
     if (args.IsArgSet("-avaproofstakeutxodustthreshold")) {
         Amount amount = Amount::zero();
         auto parsed = ParseMoney(
             args.GetArg("-avaproofstakeutxodustthreshold", ""), amount);
         if (!parsed || Amount::zero() == amount) {
             return InitError(AmountErrMsg(
                 "avaproofstakeutxodustthreshold",
                 args.GetArg("-avaproofstakeutxodustthreshold", "")));
         }
 
         if (!chainparams.IsTestChain() &&
             amount != avalanche::PROOF_DUST_THRESHOLD) {
             return InitError(_("Avalanche stake UTXO dust threshold can "
                                "only be set on test chains."));
         }
     }
 
     // This is a staking node
     if (isAvalancheEnabled(args) && args.IsArgSet("-avaproof")) {
         if (!args.GetBoolArg("-listen", true)) {
             return InitError(_("Running a staking node requires accepting "
                                "inbound connections. Please enable -listen."));
         }
         if (args.IsArgSet("-proxy")) {
             return InitError(_("Running a staking node behind a proxy is not "
                                "supported. Please disable -proxy."));
         }
         if (args.IsArgSet("-i2psam")) {
             return InitError(_("Running a staking node behind I2P is not "
                                "supported. Please disable -i2psam."));
         }
         if (args.IsArgSet("-onlynet")) {
             return InitError(
                 _("Restricting the outbound network is not supported when "
                   "running a staking node. Please disable -onlynet."));
         }
     }
 
     // Also report errors from parsing before daemonization
     {
         ChainstateManager::Options chainman_opts_dummy{
             .config = config,
         };
         if (const auto error{ApplyArgsManOptions(args, chainman_opts_dummy)}) {
             return InitError(*error);
         }
     }
 
     return true;
 }
 
 static bool LockDataDirectory(bool probeOnly) {
     // Make sure only a single Bitcoin process is using the data directory.
     fs::path datadir = gArgs.GetDataDirNet();
     if (!DirIsWritable(datadir)) {
         return InitError(strprintf(
             _("Cannot write to data directory '%s'; check permissions."),
             fs::PathToString(datadir)));
     }
     if (!LockDirectory(datadir, ".lock", probeOnly)) {
         return InitError(strprintf(_("Cannot obtain a lock on data directory "
                                      "%s. %s is probably already running."),
                                    fs::PathToString(datadir), PACKAGE_NAME));
     }
     return true;
 }
 
 bool AppInitSanityChecks() {
     // Step 4: sanity checks
 
     init::SetGlobals();
 
     // Sanity check
     if (!init::SanityChecks()) {
         return InitError(strprintf(
             _("Initialization sanity check failed. %s is shutting down."),
             PACKAGE_NAME));
     }
 
     // Probe the data directory lock to give an early error message, if possible
     // We cannot hold the data directory lock here, as the forking for daemon()
     // hasn't yet happened, and a fork will cause weird behavior to it.
     return LockDataDirectory(true);
 }
 
 bool AppInitLockDataDirectory() {
     // After daemonization get the data directory lock again and hold on to it
     // until exit. This creates a slight window for a race condition to happen,
     // however this condition is harmless: it will at most make us exit without
     // printing a message to console.
     if (!LockDataDirectory(false)) {
         // Detailed error printed inside LockDataDirectory
         return false;
     }
     return true;
 }
 
 bool AppInitInterfaces(NodeContext &node) {
     node.chain = interfaces::MakeChain(node, Params());
     // Create client interfaces for wallets that are supposed to be loaded
     // according to -wallet and -disablewallet options. This only constructs
     // the interfaces, it doesn't load wallet data. Wallets actually get loaded
     // when load() and start() interface methods are called below.
     g_wallet_init_interface.Construct(node);
     return true;
 }
 
 bool AppInitMain(Config &config, RPCServer &rpcServer,
                  HTTPRPCRequestProcessor &httpRPCRequestProcessor,
                  NodeContext &node,
                  interfaces::BlockAndHeaderTipInfo *tip_info) {
     // Step 4a: application initialization
     const ArgsManager &args = *Assert(node.args);
     const CChainParams &chainparams = config.GetChainParams();
 
     if (!CreatePidFile(args)) {
         // Detailed error printed inside CreatePidFile().
         return false;
     }
     if (!init::StartLogging(args)) {
         // Detailed error printed inside StartLogging().
         return false;
     }
 
     LogPrintf("Using at most %i automatic connections (%i file descriptors "
               "available)\n",
               nMaxConnections, nFD);
 
     // Warn about relative -datadir path.
     if (args.IsArgSet("-datadir") &&
         !args.GetPathArg("-datadir").is_absolute()) {
         LogPrintf("Warning: relative datadir option '%s' specified, which will "
                   "be interpreted relative to the current working directory "
                   "'%s'. This is fragile, because if bitcoin is started in the "
                   "future from a different location, it will be unable to "
                   "locate the current data files. There could also be data "
                   "loss if bitcoin is started while in a temporary "
                   "directory.\n",
                   args.GetArg("-datadir", ""),
                   fs::PathToString(fs::current_path()));
     }
 
     ValidationCacheSizes validation_cache_sizes{};
     ApplyArgsManOptions(args, validation_cache_sizes);
 
     if (!InitSignatureCache(validation_cache_sizes.signature_cache_bytes)) {
         return InitError(strprintf(
             _("Unable to allocate memory for -maxsigcachesize: '%s' MiB"),
             args.GetIntArg("-maxsigcachesize",
                            DEFAULT_MAX_SIG_CACHE_BYTES >> 20)));
     }
     if (!InitScriptExecutionCache(
             validation_cache_sizes.script_execution_cache_bytes)) {
         return InitError(strprintf(
             _("Unable to allocate memory for -maxscriptcachesize: '%s' MiB"),
             args.GetIntArg("-maxscriptcachesize",
                            DEFAULT_MAX_SCRIPT_CACHE_BYTES >> 20)));
     }
 
     int script_threads = args.GetIntArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
     if (script_threads <= 0) {
         // -par=0 means autodetect (number of cores - 1 script threads)
         // -par=-n means "leave n cores free" (number of cores - n - 1 script
         // threads)
         script_threads += GetNumCores();
     }
 
     // Subtract 1 because the main thread counts towards the par threads
     script_threads = std::max(script_threads - 1, 0);
 
     // Number of script-checking threads <= MAX_SCRIPTCHECK_THREADS
     script_threads = std::min(script_threads, MAX_SCRIPTCHECK_THREADS);
 
     LogPrintf("Script verification uses %d additional threads\n",
               script_threads);
     if (script_threads >= 1) {
         StartScriptCheckWorkerThreads(script_threads);
     }
 
     assert(!node.scheduler);
     node.scheduler = std::make_unique<CScheduler>();
 
     // Start the lightweight task scheduler thread
     node.scheduler->m_service_thread =
         std::thread(&util::TraceThread, "scheduler",
                     [&] { node.scheduler->serviceQueue(); });
 
     // Gather some entropy once per minute.
     node.scheduler->scheduleEvery(
         [] {
             RandAddPeriodic();
             return true;
         },
         std::chrono::minutes{1});
 
     GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
 
     /**
      * Register RPC commands regardless of -server setting so they will be
      * available in the GUI RPC console even if external calls are disabled.
      */
     RegisterAllRPCCommands(config, rpcServer, tableRPC);
     for (const auto &client : node.chain_clients) {
         client->registerRpcs();
     }
 #if ENABLE_ZMQ
     RegisterZMQRPCCommands(tableRPC);
 #endif
 
     /**
      * Start the RPC server.  It will be started in "warmup" mode and not
      * process calls yet (but it will verify that the server is there and will
      * be ready later).  Warmup mode will be completed when initialisation is
      * finished.
      */
     if (args.GetBoolArg("-server", false)) {
         uiInterface.InitMessage_connect(SetRPCWarmupStatus);
         if (!AppInitServers(config, httpRPCRequestProcessor, node)) {
             return InitError(
                 _("Unable to start HTTP server. See debug log for details."));
         }
     }
 
     // Step 5: verify wallet database integrity
     for (const auto &client : node.chain_clients) {
         if (!client->verify()) {
             return false;
         }
     }
 
     // Step 6: network initialization
 
     // Note that we absolutely cannot open any actual connections
     // until the very end ("start node") as the UTXO/block state
     // is not yet setup and may end up being set up twice if we
     // need to reindex later.
 
     fListen = args.GetBoolArg("-listen", DEFAULT_LISTEN);
     fDiscover = args.GetBoolArg("-discover", true);
 
     {
         // Initialize addrman
         assert(!node.addrman);
 
         // Read asmap file if configured
         std::vector<bool> asmap;
         if (args.IsArgSet("-asmap")) {
             fs::path asmap_path =
                 args.GetPathArg("-asmap", DEFAULT_ASMAP_FILENAME);
             if (!asmap_path.is_absolute()) {
                 asmap_path = args.GetDataDirNet() / asmap_path;
             }
             if (!fs::exists(asmap_path)) {
                 InitError(strprintf(_("Could not find asmap file %s"),
                                     fs::quoted(fs::PathToString(asmap_path))));
                 return false;
             }
             asmap = DecodeAsmap(asmap_path);
             if (asmap.size() == 0) {
                 InitError(strprintf(_("Could not parse asmap file %s"),
                                     fs::quoted(fs::PathToString(asmap_path))));
                 return false;
             }
             const uint256 asmap_version = SerializeHash(asmap);
             LogPrintf("Using asmap version %s for IP bucketing\n",
                       asmap_version.ToString());
         } else {
             LogPrintf("Using /16 prefix for IP bucketing\n");
         }
 
         uiInterface.InitMessage(_("Loading P2P addresses...").translated);
         auto addrman{LoadAddrman(chainparams, asmap, args)};
         if (!addrman) {
             return InitError(util::ErrorString(addrman));
         }
         node.addrman = std::move(*addrman);
     }
 
     assert(!node.banman);
     node.banman = std::make_unique<BanMan>(
         args.GetDataDirNet() / "banlist.dat", config.GetChainParams(),
         &uiInterface, args.GetIntArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
     assert(!node.connman);
     node.connman = std::make_unique<CConnman>(
         config, GetRand<uint64_t>(), GetRand<uint64_t>(), *node.addrman,
         args.GetBoolArg("-networkactive", true));
 
     // sanitize comments per BIP-0014, format user agent and check total size
     std::vector<std::string> uacomments;
     for (const std::string &cmt : args.GetArgs("-uacomment")) {
         if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) {
             return InitError(strprintf(
                 _("User Agent comment (%s) contains unsafe characters."), cmt));
         }
         uacomments.push_back(cmt);
     }
     const std::string client_name = args.GetArg("-uaclientname", CLIENT_NAME);
     const std::string client_version =
         args.GetArg("-uaclientversion", FormatVersion(CLIENT_VERSION));
     if (client_name != SanitizeString(client_name, SAFE_CHARS_UA_COMMENT)) {
         return InitError(strprintf(
             _("-uaclientname (%s) contains invalid characters."), client_name));
     }
     if (client_version !=
         SanitizeString(client_version, SAFE_CHARS_UA_COMMENT)) {
         return InitError(
             strprintf(_("-uaclientversion (%s) contains invalid characters."),
                       client_version));
     }
     const std::string strSubVersion =
         FormatUserAgent(client_name, client_version, uacomments);
     if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) {
         return InitError(strprintf(
             _("Total length of network version string (%i) exceeds maximum "
               "length (%i). Reduce the number or size of uacomments."),
             strSubVersion.size(), MAX_SUBVERSION_LENGTH));
     }
 
     if (args.IsArgSet("-onlynet")) {
         std::set<enum Network> nets;
         for (const std::string &snet : args.GetArgs("-onlynet")) {
             enum Network net = ParseNetwork(snet);
             if (net == NET_UNROUTABLE) {
                 return InitError(strprintf(
                     _("Unknown network specified in -onlynet: '%s'"), snet));
             }
             nets.insert(net);
         }
         for (int n = 0; n < NET_MAX; n++) {
             enum Network net = (enum Network)n;
             if (!nets.count(net)) {
                 SetReachable(net, false);
             }
         }
     }
 
     // Check for host lookup allowed before parsing any network related
     // parameters
     fNameLookup = args.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
 
     bool proxyRandomize =
         args.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
     // -proxy sets a proxy for all outgoing network traffic
     // -noproxy (or -proxy=0) as well as the empty string can be used to not set
     // a proxy, this is the default
     std::string proxyArg = args.GetArg("-proxy", "");
     SetReachable(NET_ONION, false);
     if (proxyArg != "" && proxyArg != "0") {
         CService proxyAddr;
         if (!Lookup(proxyArg, proxyAddr, 9050, fNameLookup)) {
             return InitError(strprintf(
                 _("Invalid -proxy address or hostname: '%s'"), proxyArg));
         }
 
         proxyType addrProxy = proxyType(proxyAddr, proxyRandomize);
         if (!addrProxy.IsValid()) {
             return InitError(strprintf(
                 _("Invalid -proxy address or hostname: '%s'"), proxyArg));
         }
 
         SetProxy(NET_IPV4, addrProxy);
         SetProxy(NET_IPV6, addrProxy);
         SetProxy(NET_ONION, addrProxy);
         SetNameProxy(addrProxy);
         // by default, -proxy sets onion as reachable, unless -noonion later
         SetReachable(NET_ONION, true);
     }
 
     // -onion can be used to set only a proxy for .onion, or override normal
     // proxy for .onion addresses.
     // -noonion (or -onion=0) disables connecting to .onion entirely. An empty
     // string is used to not override the onion proxy (in which case it defaults
     // to -proxy set above, or none)
     std::string onionArg = args.GetArg("-onion", "");
     if (onionArg != "") {
         if (onionArg == "0") {
             // Handle -noonion/-onion=0
             SetReachable(NET_ONION, false);
         } else {
             CService onionProxy;
             if (!Lookup(onionArg, onionProxy, 9050, fNameLookup)) {
                 return InitError(strprintf(
                     _("Invalid -onion address or hostname: '%s'"), onionArg));
             }
             proxyType addrOnion = proxyType(onionProxy, proxyRandomize);
             if (!addrOnion.IsValid()) {
                 return InitError(strprintf(
                     _("Invalid -onion address or hostname: '%s'"), onionArg));
             }
             SetProxy(NET_ONION, addrOnion);
             SetReachable(NET_ONION, true);
         }
     }
 
     for (const std::string &strAddr : args.GetArgs("-externalip")) {
         CService addrLocal;
         if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) &&
             addrLocal.IsValid()) {
             AddLocal(addrLocal, LOCAL_MANUAL);
         } else {
             return InitError(ResolveErrMsg("externalip", strAddr));
         }
     }
 
 #if ENABLE_ZMQ
     g_zmq_notification_interface = CZMQNotificationInterface::Create();
 
     if (g_zmq_notification_interface) {
         RegisterValidationInterface(g_zmq_notification_interface);
     }
 #endif
 
     // Step 7: load block chain
 
     fReindex = args.GetBoolArg("-reindex", false);
     bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
 
     ChainstateManager::Options chainman_opts{
         .config = config,
         .adjusted_time_callback = GetAdjustedTime,
     };
     // no error can happen, already checked in AppInitParameterInteraction
     Assert(!ApplyArgsManOptions(args, chainman_opts));
 
     if (chainman_opts.checkpoints_enabled) {
         LogPrintf("Checkpoints will be verified.\n");
     } else {
         LogPrintf("Skipping checkpoint verification.\n");
     }
 
     // cache size calculations
     CacheSizes cache_sizes =
         CalculateCacheSizes(args, g_enabled_filter_types.size());
 
     LogPrintf("Cache configuration:\n");
     LogPrintf("* Using %.1f MiB for block index database\n",
               cache_sizes.block_tree_db * (1.0 / 1024 / 1024));
     if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
         LogPrintf("* Using %.1f MiB for transaction index database\n",
                   cache_sizes.tx_index * (1.0 / 1024 / 1024));
     }
     for (BlockFilterType filter_type : g_enabled_filter_types) {
         LogPrintf("* Using %.1f MiB for %s block filter index database\n",
                   cache_sizes.filter_index * (1.0 / 1024 / 1024),
                   BlockFilterTypeName(filter_type));
     }
     LogPrintf("* Using %.1f MiB for chain state database\n",
               cache_sizes.coins_db * (1.0 / 1024 / 1024));
 
     assert(!node.mempool);
     assert(!node.chainman);
 
     CTxMemPool::Options mempool_opts{
         .check_ratio = chainparams.DefaultConsistencyChecks() ? 1 : 0,
     };
     if (const auto err{ApplyArgsManOptions(args, chainparams, mempool_opts)}) {
         return InitError(*err);
     }
     mempool_opts.check_ratio =
         std::clamp<int>(mempool_opts.check_ratio, 0, 1'000'000);
 
     // FIXME: this legacy limit comes from the DEFAULT_DESCENDANT_SIZE_LIMIT
     // (101) that was enforced before the wellington activation. While it's
     // still a good idea to have some minimum mempool size, using this value as
     // a threshold is no longer relevant.
     int64_t nMempoolSizeMin = 101 * 1000 * 40;
     if (mempool_opts.max_size_bytes < 0 ||
         (!chainparams.IsTestChain() &&
          mempool_opts.max_size_bytes < nMempoolSizeMin)) {
         return InitError(strprintf(_("-maxmempool must be at least %d MB"),
                                    std::ceil(nMempoolSizeMin / 1000000.0)));
     }
     LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of "
               "unused mempool space)\n",
               cache_sizes.coins * (1.0 / 1024 / 1024),
               mempool_opts.max_size_bytes * (1.0 / 1024 / 1024));
 
     for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) {
         node.mempool = std::make_unique<CTxMemPool>(mempool_opts);
 
         node.chainman = std::make_unique<ChainstateManager>(chainman_opts);
         ChainstateManager &chainman = *node.chainman;
 
         node::ChainstateLoadOptions options;
         options.mempool = Assert(node.mempool.get());
         options.reindex = node::fReindex;
         options.reindex_chainstate = fReindexChainState;
         options.prune = chainman.m_blockman.IsPruneMode();
         options.check_blocks =
             args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
         options.check_level = args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
         options.require_full_verification =
             args.IsArgSet("-checkblocks") || args.IsArgSet("-checklevel");
         options.check_interrupt = ShutdownRequested;
         options.coins_error_cb = [] {
             uiInterface.ThreadSafeMessageBox(
                 _("Error reading from database, shutting down."), "",
                 CClientUIInterface::MSG_ERROR);
         };
 
         uiInterface.InitMessage(_("Loading block index...").translated);
 
         const int64_t load_block_index_start_time = GetTimeMillis();
         auto catch_exceptions = [](auto &&f) {
             try {
                 return f();
             } catch (const std::exception &e) {
                 LogPrintf("%s\n", e.what());
                 return std::make_tuple(node::ChainstateLoadStatus::FAILURE,
                                        _("Error opening block database"));
             }
         };
         auto [status, error] = catch_exceptions(
             [&] { return LoadChainstate(chainman, cache_sizes, options); });
         if (status == node::ChainstateLoadStatus::SUCCESS) {
             uiInterface.InitMessage(_("Verifying blocks...").translated);
             if (chainman.m_blockman.m_have_pruned &&
                 options.check_blocks > MIN_BLOCKS_TO_KEEP) {
                 LogPrintf("Prune: pruned datadir may not have more than %d "
                           "blocks; only checking available blocks\n",
                           MIN_BLOCKS_TO_KEEP);
             }
             std::tie(status, error) = catch_exceptions(
                 [&] { return VerifyLoadedChainstate(chainman, options); });
             if (status == node::ChainstateLoadStatus::SUCCESS) {
                 fLoaded = true;
                 LogPrintf(" block index %15dms\n",
                           GetTimeMillis() - load_block_index_start_time);
             }
         }
 
         if (status == node::ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB ||
             status ==
                 node::ChainstateLoadStatus::FAILURE_INSUFFICIENT_DBCACHE) {
             return InitError(error);
         }
 
         if (!fLoaded && !ShutdownRequested()) {
             // first suggest a reindex
             if (!options.reindex) {
                 bool fRet = uiInterface.ThreadSafeQuestion(
                     error + Untranslated(".\n\n") +
                         _("Do you want to rebuild the block database now?"),
                     error.original + ".\nPlease restart with -reindex or "
                                      "-reindex-chainstate to recover.",
                     "",
                     CClientUIInterface::MSG_ERROR |
                         CClientUIInterface::BTN_ABORT);
                 if (fRet) {
                     fReindex = true;
                     AbortShutdown();
                 } else {
                     LogPrintf("Aborted block database rebuild. Exiting.\n");
                     return false;
                 }
             } else {
                 return InitError(error);
             }
         }
     }
 
     // As LoadBlockIndex can take several minutes, it's possible the user
     // requested to kill the GUI during the last operation. If so, exit.
     // As the program has not fully started yet, Shutdown() is possibly
     // overkill.
     if (ShutdownRequested()) {
         LogPrintf("Shutdown requested. Exiting.\n");
         return false;
     }
 
     ChainstateManager &chainman = *Assert(node.chainman);
 
     assert(!node.peerman);
     node.peerman = PeerManager::make(
         *node.connman, *node.addrman, node.banman.get(), chainman,
         *node.mempool, args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY));
     RegisterValidationInterface(node.peerman.get());
 
     // Encoded addresses using cashaddr instead of base58.
     // We do this by default to avoid confusion with BTC addresses.
     config.SetCashAddrEncoding(args.GetBoolArg("-usecashaddr", true));
 
     // Step 7.5 (I guess ?): Initialize Avalanche.
     bilingual_str avalancheError;
     g_avalanche = avalanche::Processor::MakeProcessor(
         args, *node.chain, node.connman.get(), chainman, node.mempool.get(),
         *node.scheduler, avalancheError);
     if (!g_avalanche) {
         InitError(avalancheError);
         return false;
     }
 
     if (isAvalancheEnabled(args) &&
         g_avalanche->isAvalancheServiceAvailable()) {
         nLocalServices = ServiceFlags(nLocalServices | NODE_AVALANCHE);
     }
 
     // Step 8: load indexers
     if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
         auto result{
             WITH_LOCK(cs_main, return CheckLegacyTxindex(*Assert(
                                    chainman.m_blockman.m_block_tree_db)))};
         if (!result) {
             return InitError(util::ErrorString(result));
         }
 
         g_txindex =
             std::make_unique<TxIndex>(cache_sizes.tx_index, false, fReindex);
         if (!g_txindex->Start(chainman.ActiveChainstate())) {
             return false;
         }
     }
 
     for (const auto &filter_type : g_enabled_filter_types) {
         InitBlockFilterIndex(filter_type, cache_sizes.filter_index, false,
                              fReindex);
         if (!GetBlockFilterIndex(filter_type)
                  ->Start(chainman.ActiveChainstate())) {
             return false;
         }
     }
 
     if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
         g_coin_stats_index = std::make_unique<CoinStatsIndex>(
             /* cache size */ 0, false, fReindex);
         if (!g_coin_stats_index->Start(chainman.ActiveChainstate())) {
             return false;
         }
     }
 
 #if ENABLE_CHRONIK
     if (args.GetBoolArg("-chronik", DEFAULT_CHRONIK)) {
         const bool fReindexChronik =
             fReindex || args.GetBoolArg("-chronikreindex", false);
         if (!chronik::Start(config, node, fReindexChronik)) {
             return false;
         }
     }
 #endif
 
     // Step 9: load wallet
     for (const auto &client : node.chain_clients) {
         if (!client->load()) {
             return false;
         }
     }
 
     // Step 10: data directory maintenance
 
     // if pruning, unset the service bit and perform the initial blockstore
     // prune after any wallet rescanning has taken place.
     if (chainman.m_blockman.IsPruneMode()) {
         LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
         nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
         if (!fReindex) {
             LOCK(cs_main);
             for (Chainstate *chainstate : chainman.GetAll()) {
                 uiInterface.InitMessage(_("Pruning blockstore...").translated);
                 chainstate->PruneAndFlush();
             }
         }
     }
 
     // Step 11: import blocks
     if (!CheckDiskSpace(args.GetDataDirNet())) {
         InitError(
             strprintf(_("Error: Disk space is low for %s"),
                       fs::quoted(fs::PathToString(args.GetDataDirNet()))));
         return false;
     }
     if (!CheckDiskSpace(args.GetBlocksDirPath())) {
         InitError(
             strprintf(_("Error: Disk space is low for %s"),
                       fs::quoted(fs::PathToString(args.GetBlocksDirPath()))));
         return false;
     }
 
     // Either install a handler to notify us when genesis activates, or set
     // fHaveGenesis directly.
     // No locking, as this happens before any background thread is started.
     boost::signals2::connection block_notify_genesis_wait_connection;
     if (WITH_LOCK(chainman.GetMutex(),
                   return chainman.ActiveChain().Tip() == nullptr)) {
         block_notify_genesis_wait_connection =
             uiInterface.NotifyBlockTip_connect(
                 std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
     } else {
         fHaveGenesis = true;
     }
 
 #if defined(HAVE_SYSTEM)
     const std::string block_notify = args.GetArg("-blocknotify", "");
     if (!block_notify.empty()) {
         uiInterface.NotifyBlockTip_connect([block_notify](
                                                SynchronizationState sync_state,
                                                const CBlockIndex *pBlockIndex) {
             if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex) {
                 return;
             }
             std::string command = block_notify;
             ReplaceAll(command, "%s", pBlockIndex->GetBlockHash().GetHex());
             std::thread t(runCommand, command);
             // thread runs free
             t.detach();
         });
     }
 #endif
 
     std::vector<fs::path> vImportFiles;
     for (const std::string &strFile : args.GetArgs("-loadblock")) {
         vImportFiles.push_back(fs::PathFromString(strFile));
     }
 
     chainman.m_load_block =
         std::thread(&util::TraceThread, "loadblk", [=, &chainman, &args] {
             ThreadImport(chainman, vImportFiles, args,
                          ShouldPersistMempool(args) ? MempoolPath(args)
                                                     : fs::path{});
         });
 
     // Wait for genesis block to be processed
     {
         WAIT_LOCK(g_genesis_wait_mutex, lock);
         // We previously could hang here if StartShutdown() is called prior to
         // ThreadImport getting started, so instead we just wait on a timer to
         // check ShutdownRequested() regularly.
         while (!fHaveGenesis && !ShutdownRequested()) {
             g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
         }
         block_notify_genesis_wait_connection.disconnect();
     }
 
     if (ShutdownRequested()) {
         return false;
     }
 
     // Step 12: start node
 
     int chain_active_height;
 
     //// debug print
     {
         LOCK(cs_main);
         LogPrintf("block tree size = %u\n", chainman.BlockIndex().size());
         chain_active_height = chainman.ActiveChain().Height();
         if (tip_info) {
             tip_info->block_height = chain_active_height;
             tip_info->block_time =
                 chainman.ActiveChain().Tip()
                     ? chainman.ActiveChain().Tip()->GetBlockTime()
                     : chainman.GetParams().GenesisBlock().GetBlockTime();
             tip_info->verification_progress = GuessVerificationProgress(
                 chainman.GetParams().TxData(), chainman.ActiveChain().Tip());
         }
         if (tip_info && chainman.m_best_header) {
             tip_info->header_height = chainman.m_best_header->nHeight;
             tip_info->header_time = chainman.m_best_header->GetBlockTime();
         }
     }
     LogPrintf("nBestHeight = %d\n", chain_active_height);
     if (node.peerman) {
         node.peerman->SetBestHeight(chain_active_height);
     }
 
     // Map ports with UPnP or NAT-PMP.
     StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP),
                  args.GetBoolArg("-natpmp", DEFAULT_NATPMP));
 
     CConnman::Options connOptions;
     connOptions.nLocalServices = nLocalServices;
     connOptions.nMaxConnections = nMaxConnections;
     connOptions.m_max_avalanche_outbound =
         g_avalanche && isAvalancheEnabled(args)
             ? args.GetIntArg("-maxavalancheoutbound",
                              DEFAULT_MAX_AVALANCHE_OUTBOUND_CONNECTIONS)
             : 0;
     connOptions.m_max_outbound_full_relay = std::min(
         MAX_OUTBOUND_FULL_RELAY_CONNECTIONS,
         connOptions.nMaxConnections - connOptions.m_max_avalanche_outbound);
     connOptions.m_max_outbound_block_relay = std::min(
         MAX_BLOCK_RELAY_ONLY_CONNECTIONS,
         connOptions.nMaxConnections - connOptions.m_max_avalanche_outbound -
             connOptions.m_max_outbound_full_relay);
     connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
     connOptions.nMaxFeeler = MAX_FEELER_CONNECTIONS;
     connOptions.uiInterface = &uiInterface;
     connOptions.m_banman = node.banman.get();
     connOptions.m_msgproc.push_back(node.peerman.get());
     if (g_avalanche) {
         connOptions.m_msgproc.push_back(g_avalanche.get());
     }
     connOptions.nSendBufferMaxSize =
         1000 * args.GetIntArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
     connOptions.nReceiveFloodSize =
         1000 * args.GetIntArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
     connOptions.m_added_nodes = args.GetArgs("-addnode");
 
     connOptions.nMaxOutboundLimit =
         1024 * 1024 *
         args.GetIntArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET);
     connOptions.m_peer_connect_timeout = peer_connect_timeout;
 
     // Port to bind to if `-bind=addr` is provided without a `:port` suffix.
     const uint16_t default_bind_port = static_cast<uint16_t>(
         args.GetIntArg("-port", config.GetChainParams().GetDefaultPort()));
 
     const auto BadPortWarning = [](const char *prefix, uint16_t port) {
         return strprintf(_("%s request to listen on port %u. This port is "
                            "considered \"bad\" and "
                            "thus it is unlikely that any Bitcoin ABC peers "
                            "connect to it. See "
                            "doc/p2p-bad-ports.md for details and a full list."),
                          prefix, port);
     };
 
     for (const std::string &bind_arg : args.GetArgs("-bind")) {
         CService bind_addr;
         const size_t index = bind_arg.rfind('=');
         if (index == std::string::npos) {
             if (Lookup(bind_arg, bind_addr, default_bind_port,
                        /*fAllowLookup=*/false)) {
                 connOptions.vBinds.push_back(bind_addr);
                 if (IsBadPort(bind_addr.GetPort())) {
                     InitWarning(BadPortWarning("-bind", bind_addr.GetPort()));
                 }
                 continue;
             }
         } else {
             const std::string network_type = bind_arg.substr(index + 1);
             if (network_type == "onion") {
                 const std::string truncated_bind_arg =
                     bind_arg.substr(0, index);
                 if (Lookup(truncated_bind_arg, bind_addr,
                            BaseParams().OnionServiceTargetPort(), false)) {
                     connOptions.onion_binds.push_back(bind_addr);
                     continue;
                 }
             }
         }
         return InitError(ResolveErrMsg("bind", bind_arg));
     }
 
     for (const std::string &strBind : args.GetArgs("-whitebind")) {
         NetWhitebindPermissions whitebind;
         bilingual_str error;
         if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) {
             return InitError(error);
         }
         connOptions.vWhiteBinds.push_back(whitebind);
     }
 
     // If the user did not specify -bind= or -whitebind= then we bind
     // on any address - 0.0.0.0 (IPv4) and :: (IPv6).
     connOptions.bind_on_any =
         args.GetArgs("-bind").empty() && args.GetArgs("-whitebind").empty();
 
     // Emit a warning if a bad port is given to -port= but only if -bind and
     // -whitebind are not given, because if they are, then -port= is ignored.
     if (connOptions.bind_on_any && args.IsArgSet("-port")) {
         const uint16_t port_arg = args.GetIntArg("-port", 0);
         if (IsBadPort(port_arg)) {
             InitWarning(BadPortWarning("-port", port_arg));
         }
     }
 
     CService onion_service_target;
     if (!connOptions.onion_binds.empty()) {
         onion_service_target = connOptions.onion_binds.front();
     } else {
         onion_service_target = DefaultOnionServiceTarget();
         connOptions.onion_binds.push_back(onion_service_target);
     }
 
     if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
         if (connOptions.onion_binds.size() > 1) {
             InitWarning(strprintf(
                 _("More than one onion bind address is provided. Using %s "
                   "for the automatically created Tor onion service."),
                 onion_service_target.ToStringIPPort()));
         }
         StartTorControl(onion_service_target);
     }
 
     if (connOptions.bind_on_any) {
         // Only add all IP addresses of the machine if we would be listening on
         // any address - 0.0.0.0 (IPv4) and :: (IPv6).
         Discover();
     }
 
     for (const auto &net : args.GetArgs("-whitelist")) {
         NetWhitelistPermissions subnet;
         bilingual_str error;
         if (!NetWhitelistPermissions::TryParse(net, subnet, error)) {
             return InitError(error);
         }
         connOptions.vWhitelistedRange.push_back(subnet);
     }
 
     connOptions.vSeedNodes = args.GetArgs("-seednode");
 
     // Initiate outbound connections unless connect=0
     connOptions.m_use_addrman_outgoing = !args.IsArgSet("-connect");
     if (!connOptions.m_use_addrman_outgoing) {
         const auto connect = args.GetArgs("-connect");
         if (connect.size() != 1 || connect[0] != "0") {
             connOptions.m_specified_outgoing = connect;
         }
     }
 
     const std::string &i2psam_arg = args.GetArg("-i2psam", "");
     if (!i2psam_arg.empty()) {
         CService addr;
         if (!Lookup(i2psam_arg, addr, 7656, fNameLookup) || !addr.IsValid()) {
             return InitError(strprintf(
                 _("Invalid -i2psam address or hostname: '%s'"), i2psam_arg));
         }
         SetReachable(NET_I2P, true);
         SetProxy(NET_I2P, proxyType{addr});
     } else {
         SetReachable(NET_I2P, false);
     }
 
     connOptions.m_i2p_accept_incoming =
         args.GetBoolArg("-i2pacceptincoming", true);
 
     if (!node.connman->Start(*node.scheduler, connOptions)) {
         return false;
     }
 
     // Step 13: finished
 
     // At this point, the RPC is "started", but still in warmup, which means it
     // cannot yet be called. Before we make it callable, we need to make sure
     // that the RPC's view of the best block is valid and consistent with
     // ChainstateManager's active tip.
     //
     // If we do not do this, RPC's view of the best block will be height=0 and
     // hash=0x0. This will lead to erroroneous responses for things like
     // waitforblockheight.
     RPCNotifyBlockChange(
         WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()));
     SetRPCWarmupFinished();
 
     uiInterface.InitMessage(_("Done loading").translated);
 
     for (const auto &client : node.chain_clients) {
         client->start(*node.scheduler);
     }
 
     BanMan *banman = node.banman.get();
     node.scheduler->scheduleEvery(
         [banman] {
             banman->DumpBanlist();
             return true;
         },
         DUMP_BANS_INTERVAL);
 
     if (node.peerman) {
         node.peerman->StartScheduledTasks(*node.scheduler);
     }
 
     // Start Avalanche's event loop.
     g_avalanche->startEventLoop(*node.scheduler);
 
 #if HAVE_SYSTEM
     StartupNotify(args);
 #endif
 
     return true;
 }