diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index b8ed9aa2e..2ae161bf7 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -1,209 +1,211 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2016 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 "chainparams.h"
 #include "clientversion.h"
 #include "compat.h"
 #include "config.h"
 #include "fs.h"
 #include "httprpc.h"
 #include "httpserver.h"
 #include "init.h"
 #include "noui.h"
 #include "rpc/server.h"
 #include "scheduler.h"
 #include "util.h"
 #include "utilstrencodings.h"
 
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/thread.hpp>
 
 #include <cstdio>
 
 /* Introduction text for doxygen: */
 
 /*! \mainpage Developer documentation
  *
  * \section intro_sec Introduction
  *
  * This is the developer documentation of Bitcoin ABC
  * (https://www.bitcoinabc.org/). Bitcoin ABC is a client for the digital
  * currency called Bitcoin Cash (https://www.bitcoincash.org/), which enables
  * instant payments to anyone, anywhere in the world. Bitcoin Cash uses
  * peer-to-peer technology to operate with no central authority: managing
  * transactions and issuing money are carried out collectively by the network.
  *
  * The software is a community-driven open source project, released under the
  * MIT license.
  *
  * \section Navigation
  * Use the buttons <code>Namespaces</code>, <code>Classes</code> or
  * <code>Files</code> at the top of the page to start navigating the code.
  */
 
 void WaitForShutdown(boost::thread_group *threadGroup) {
     bool fShutdown = ShutdownRequested();
     // Tell the main threads to shutdown.
     while (!fShutdown) {
         MilliSleep(200);
         fShutdown = ShutdownRequested();
     }
     if (threadGroup) {
         Interrupt(*threadGroup);
         threadGroup->join_all();
     }
 }
 
 //////////////////////////////////////////////////////////////////////////////
 //
 // Start
 //
 bool AppInit(int argc, char *argv[]) {
     boost::thread_group threadGroup;
     CScheduler scheduler;
 
     // FIXME: Ideally, we'd like to build the config here, but that's currently
     // not possible as the whole application has too many global state. However,
     // this is a first step.
     auto &config = const_cast<Config &>(GetConfig());
+    HTTPRPCRequestProcessor httpRPCRequestProcessor(config);
 
     bool fRet = false;
 
     //
     // Parameters
     //
     // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's
     // main()
     gArgs.ParseParameters(argc, argv);
 
     // Process help and version before taking care about datadir
     if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") ||
         gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) {
         std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) +
                                " " + _("version") + " " + FormatFullVersion() +
                                "\n";
 
         if (gArgs.IsArgSet("-version")) {
             strUsage += FormatParagraph(LicenseInfo());
         } else {
             strUsage += "\n" + _("Usage:") + "\n" +
                         "  bitcoind [options]                     " +
                         strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";
 
             strUsage += "\n" + HelpMessage(HMM_BITCOIND);
         }
 
         fprintf(stdout, "%s", strUsage.c_str());
         return true;
     }
 
     try {
         if (!fs::is_directory(GetDataDir(false))) {
             fprintf(stderr,
                     "Error: Specified data directory \"%s\" does not exist.\n",
                     gArgs.GetArg("-datadir", "").c_str());
             return false;
         }
         try {
             gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
         } catch (const std::exception &e) {
             fprintf(stderr, "Error reading configuration file: %s\n", e.what());
             return false;
         }
         // Check for -testnet or -regtest parameter (Params() calls are only
         // valid after this clause)
         try {
             SelectParams(ChainNameFromCommandLine());
         } catch (const std::exception &e) {
             fprintf(stderr, "Error: %s\n", e.what());
             return false;
         }
 
         // Command-line RPC
         bool fCommandLine = false;
         for (int i = 1; i < argc; i++)
             if (!IsSwitchChar(argv[i][0]) &&
                 !boost::algorithm::istarts_with(argv[i], "bitcoin:"))
                 fCommandLine = true;
 
         if (fCommandLine) {
             fprintf(stderr, "Error: There is no RPC client functionality in "
                             "bitcoind anymore. Use the bitcoin-cli utility "
                             "instead.\n");
             exit(EXIT_FAILURE);
         }
         // -server defaults to true for bitcoind but not for the GUI so do this
         // here
         gArgs.SoftSetBoolArg("-server", true);
         // Set this early so that parameter interactions go to console
         InitLogging();
         InitParameterInteraction();
         if (!AppInitBasicSetup()) {
             // InitError will have been called with detailed error, which ends
             // up on console
             exit(1);
         }
         if (!AppInitParameterInteraction(config)) {
             // InitError will have been called with detailed error, which ends
             // up on console
             exit(1);
         }
         if (!AppInitSanityChecks()) {
             // InitError will have been called with detailed error, which ends
             // up on console
             exit(1);
         }
         if (gArgs.GetBoolArg("-daemon", false)) {
 #if HAVE_DECL_DAEMON
             fprintf(stdout, "Bitcoin server starting\n");
 
             // Daemonize
             if (daemon(1, 0)) {
                 // don't chdir (1), do close FDs (0)
                 fprintf(stderr, "Error: daemon() failed: %s\n",
                         strerror(errno));
                 return false;
             }
 #else
             fprintf(
                 stderr,
                 "Error: -daemon is not supported on this operating system\n");
             return false;
 #endif // HAVE_DECL_DAEMON
         }
 
-        fRet = AppInitMain(config, threadGroup, scheduler);
+        fRet = AppInitMain(config, httpRPCRequestProcessor, threadGroup,
+                           scheduler);
     } catch (const std::exception &e) {
         PrintExceptionContinue(&e, "AppInit()");
     } catch (...) {
         PrintExceptionContinue(nullptr, "AppInit()");
     }
 
     if (!fRet) {
         Interrupt(threadGroup);
         // threadGroup.join_all(); was left out intentionally here, because we
         // didn't re-test all of the startup-failure cases to make sure they
         // don't result in a hang due to some
         // thread-blocking-waiting-for-another-thread-during-startup case.
     } else {
         WaitForShutdown(&threadGroup);
     }
     Shutdown();
 
     return fRet;
 }
 
 int main(int argc, char *argv[]) {
     SetupEnvironment();
 
     // Connect bitcoind signal handlers
     noui_connect();
 
     return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
 }
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 2bc5acfac..3b19008f1 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -1,396 +1,400 @@
 // Copyright (c) 2015-2016 The Bitcoin Core developers
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 #include "httprpc.h"
 
 #include "base58.h"
 #include "chainparams.h"
 #include "config.h"
 #include "crypto/hmac_sha256.h"
 #include "httpserver.h"
 #include "random.h"
 #include "rpc/protocol.h"
 #include "rpc/server.h"
 #include "sync.h"
 #include "ui_interface.h"
 #include "util.h"
 #include "utilstrencodings.h"
 
 #include <boost/algorithm/string.hpp> // boost::trim
 
 #include <cstdio>
 
 /** WWW-Authenticate to present with 401 Unauthorized response */
 static const char *WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
 
 /** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
  * re-lock the wallet.
  */
 class HTTPRPCTimer : public RPCTimerBase {
 public:
     HTTPRPCTimer(struct event_base *eventBase, std::function<void(void)> &func,
                  int64_t millis)
         : ev(eventBase, false, func) {
         struct timeval tv;
         tv.tv_sec = millis / 1000;
         tv.tv_usec = (millis % 1000) * 1000;
         ev.trigger(&tv);
     }
 
 private:
     HTTPEvent ev;
 };
 
 class HTTPRPCTimerInterface : public RPCTimerInterface {
 public:
     HTTPRPCTimerInterface(struct event_base *_base) : base(_base) {}
     const char *Name() override { return "HTTP"; }
     RPCTimerBase *NewTimer(std::function<void(void)> &func,
                            int64_t millis) override {
         return new HTTPRPCTimer(base, func, millis);
     }
 
 private:
     struct event_base *base;
 };
 
 /* Stored RPC timer interface (for unregistration) */
 static HTTPRPCTimerInterface *httpRPCTimerInterface = 0;
 
 static void JSONErrorReply(HTTPRequest *req, const UniValue &objError,
                            const UniValue &id) {
     // Send error reply from json-rpc error object.
     int nStatus = HTTP_INTERNAL_SERVER_ERROR;
     int code = find_value(objError, "code").get_int();
 
     if (code == RPC_INVALID_REQUEST)
         nStatus = HTTP_BAD_REQUEST;
     else if (code == RPC_METHOD_NOT_FOUND)
         nStatus = HTTP_NOT_FOUND;
 
     std::string strReply = JSONRPCReply(NullUniValue, objError, id);
 
     req->WriteHeader("Content-Type", "application/json");
     req->WriteReply(nStatus, strReply);
 }
 
 // This function checks username and password against -rpcauth entries from
 // config file.
 static bool multiUserAuthorized(std::string strUserPass) {
     if (strUserPass.find(":") == std::string::npos) {
         return false;
     }
     std::string strUser = strUserPass.substr(0, strUserPass.find(":"));
     std::string strPass = strUserPass.substr(strUserPass.find(":") + 1);
 
     for (const std::string &strRPCAuth : gArgs.GetArgs("-rpcauth")) {
         // Search for multi-user login/pass "rpcauth" from config
         std::vector<std::string> vFields;
         boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
         if (vFields.size() != 3) {
             // Incorrect formatting in config file
             continue;
         }
 
         std::string strName = vFields[0];
         if (!TimingResistantEqual(strName, strUser)) {
             continue;
         }
 
         std::string strSalt = vFields[1];
         std::string strHash = vFields[2];
 
         static const unsigned int KEY_SIZE = 32;
         uint8_t out[KEY_SIZE];
 
         CHMAC_SHA256(reinterpret_cast<const uint8_t *>(strSalt.c_str()),
                      strSalt.size())
             .Write(reinterpret_cast<const uint8_t *>(strPass.c_str()),
                    strPass.size())
             .Finalize(out);
         std::vector<uint8_t> hexvec(out, out + KEY_SIZE);
         std::string strHashFromPass = HexStr(hexvec);
 
         if (TimingResistantEqual(strHashFromPass, strHash)) {
             return true;
         }
     }
     return false;
 }
 
 static bool RPCAuthorized(Config &config, const std::string &strAuth,
                           std::string &strAuthUsernameOut) {
     // Belt-and-suspenders measure if InitRPCAuthentication was not called.
     if (config.GetRPCUserAndPassword().empty()) {
         return false;
     }
 
     if (strAuth.substr(0, 6) != "Basic ") {
         return false;
     }
 
     std::string strUserPass64 = strAuth.substr(6);
     boost::trim(strUserPass64);
     std::string strUserPass = DecodeBase64(strUserPass64);
 
     if (strUserPass.find(":") != std::string::npos) {
         strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(":"));
     }
 
     // Check if authorized under single-user field
     if (TimingResistantEqual(strUserPass, config.GetRPCUserAndPassword())) {
         return true;
     }
     return multiUserAuthorized(strUserPass);
 }
 
 static bool checkCORS(Config &config, HTTPRequest *req) {
     // https://www.w3.org/TR/cors/#resource-requests
 
     // 1. If the Origin header is not present terminate this set of steps.
     // The request is outside the scope of this specification.
     std::pair<bool, std::string> origin = req->GetHeader("origin");
     if (!origin.first) {
         return false;
     }
 
     // 2. If the value of the Origin header is not a case-sensitive match for
     // any of the values in list of origins do not set any additional headers
     // and terminate this set of steps.
     // Note: Always matching is acceptable since the list of origins can be
     // unbounded.
     if (origin.second != config.GetRPCCORSDomain()) {
         return false;
     }
 
     if (req->GetRequestMethod() == HTTPRequest::OPTIONS) {
         // 6.2 Preflight Request
         // In response to a preflight request the resource indicates which
         // methods and headers (other than simple methods and simple
         // headers) it is willing to handle and whether it supports
         // credentials.
         // Resources must use the following set of steps to determine which
         // additional headers to use in the response:
 
         // 3. Let method be the value as result of parsing the
         // Access-Control-Request-Method header.
         // If there is no Access-Control-Request-Method header or if parsing
         // failed, do not set any additional headers and terminate this set
         // of steps. The request is outside the scope of this specification.
         std::pair<bool, std::string> method =
             req->GetHeader("access-control-request-method");
         if (!method.first) {
             return false;
         }
 
         // 4. Let header field-names be the values as result of parsing
         // the Access-Control-Request-Headers headers.
         // If there are no Access-Control-Request-Headers headers let header
         // field-names be the empty list.
         // If parsing failed do not set any additional headers and terminate
         // this set of steps. The request is outside the scope of this
         // specification.
         std::pair<bool, std::string> header_field_names =
             req->GetHeader("access-control-request-headers");
 
         // 5. If method is not a case-sensitive match for any of the
         // values in list of methods do not set any additional headers
         // and terminate this set of steps.
         // Note: Always matching is acceptable since the list of methods
         // can be unbounded.
         if (method.second != "POST") {
             return false;
         }
 
         // 6. If any of the header field-names is not a ASCII case-
         // insensitive match for any of the values in list of headers do not
         // set any additional headers and terminate this set of steps.
         // Note: Always matching is acceptable since the list of headers can
         // be unbounded.
         const std::string &list_of_headers = "authorization,content-type";
 
         // 7. If the resource supports credentials add a single
         // Access-Control-Allow-Origin header, with the value of the Origin
         // header as value, and add a single
         // Access-Control-Allow-Credentials header with the case-sensitive
         // string "true" as value.
         req->WriteHeader("Access-Control-Allow-Origin", origin.second);
         req->WriteHeader("Access-Control-Allow-Credentials", "true");
 
         // 8. Optionally add a single Access-Control-Max-Age header with as
         // value the amount of seconds the user agent is allowed to cache
         // the result of the request.
 
         // 9. If method is a simple method this step may be skipped.
         // Add one or more Access-Control-Allow-Methods headers consisting
         // of (a subset of) the list of methods.
         // If a method is a simple method it does not need to be listed, but
         // this is not prohibited.
         // Note: Since the list of methods can be unbounded, simply
         // returning the method indicated by
         // Access-Control-Request-Method (if supported) can be enough.
         req->WriteHeader("Access-Control-Allow-Methods", method.second);
 
         // 10. If each of the header field-names is a simple header and none
         // is Content-Type, this step may be skipped.
         // Add one or more Access-Control-Allow-Headers headers consisting
         // of (a subset of) the list of headers.
         req->WriteHeader("Access-Control-Allow-Headers",
                          header_field_names.first ? header_field_names.second
                                                   : list_of_headers);
         req->WriteReply(HTTP_OK);
         return true;
     }
 
     // 6.1 Simple Cross-Origin Request, Actual Request, and Redirects
     // In response to a simple cross-origin request or actual request the
     // resource indicates whether or not to share the response.
     // If the resource has been relocated, it indicates whether to share its
     // new URL.
     // Resources must use the following set of steps to determine which
     // additional headers to use in the response:
 
     // 3. If the resource supports credentials add a single
     // Access-Control-Allow-Origin header, with the value of the Origin
     // header as value, and add a single Access-Control-Allow-Credentials
     // header with the case-sensitive string "true" as value.
     req->WriteHeader("Access-Control-Allow-Origin", origin.second);
     req->WriteHeader("Access-Control-Allow-Credentials", "true");
 
     // 4. If the list of exposed headers is not empty add one or more
     // Access-Control-Expose-Headers headers, with as values the header
     // field names given in the list of exposed headers.
     req->WriteHeader("Access-Control-Expose-Headers", "WWW-Authenticate");
 
     return false;
 }
 
-static bool HTTPReq_JSONRPC(Config &config, HTTPRequest *req,
-                            const std::string &) {
+bool HTTPRPCRequestProcessor::ProcessHTTPRequest(HTTPRequest *req) {
     // First, check and/or set CORS headers
     if (checkCORS(config, req)) {
         return true;
     }
 
     // JSONRPC handles only POST
     if (req->GetRequestMethod() != HTTPRequest::POST) {
         req->WriteReply(HTTP_BAD_METHOD,
                         "JSONRPC server handles only POST requests");
         return false;
     }
     // Check authorization
     std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
     if (!authHeader.first) {
         req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
         req->WriteReply(HTTP_UNAUTHORIZED);
         return false;
     }
 
     JSONRPCRequest jreq;
     if (!RPCAuthorized(config, authHeader.second, jreq.authUser)) {
         LogPrintf("ThreadRPCServer incorrect password attempt from %s\n",
                   req->GetPeer().ToString());
 
         /* Deter brute-forcing.
          * If this results in a DoS the user really shouldn't have their RPC
          * port exposed. */
         MilliSleep(250);
 
         req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
         req->WriteReply(HTTP_UNAUTHORIZED);
         return false;
     }
 
     try {
         // Parse request
         UniValue valRequest;
         if (!valRequest.read(req->ReadBody()))
             throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
 
         // Set the URI
         jreq.URI = req->GetURI();
 
         std::string strReply;
         // singleton request
         if (valRequest.isObject()) {
             jreq.parse(valRequest);
 
             UniValue result = tableRPC.execute(config, jreq);
 
             // Send reply
             strReply = JSONRPCReply(result, NullUniValue, jreq.id);
         } else if (valRequest.isArray()) {
             // array of requests
             strReply = JSONRPCExecBatch(config, jreq, valRequest.get_array());
         } else {
             throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
         }
 
         req->WriteHeader("Content-Type", "application/json");
         req->WriteReply(HTTP_OK, strReply);
     } catch (const UniValue &objError) {
         JSONErrorReply(req, objError, jreq.id);
         return false;
     } catch (const std::exception &e) {
         JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
         return false;
     }
     return true;
 }
 
 static bool InitRPCAuthentication(Config &config) {
     if (gArgs.GetArg("-rpcpassword", "") == "") {
         LogPrintf("No rpcpassword set - using random cookie authentication\n");
         std::string generatedUserAndPassword;
         if (!GenerateAuthCookie(&generatedUserAndPassword)) {
             // Same message as AbortNode.
             uiInterface.ThreadSafeMessageBox(
                 _("Error: A fatal internal error occurred, see debug.log for "
                   "details"),
                 "", CClientUIInterface::MSG_ERROR);
             return false;
         }
         config.SetRPCUserAndPassword(generatedUserAndPassword);
     } else {
         LogPrintf("Config options rpcuser and rpcpassword will soon be "
                   "deprecated. Locally-run instances may remove rpcuser to use "
                   "cookie-based auth, or may be replaced with rpcauth. Please "
                   "see share/rpcuser for rpcauth auth generation.\n");
         config.SetRPCUserAndPassword(gArgs.GetArg("-rpcuser", "") + ":" +
                                      gArgs.GetArg("-rpcpassword", ""));
     }
 
     config.SetRPCCORSDomain(gArgs.GetArg("-rpccorsdomain", ""));
     return true;
 }
 
-bool StartHTTPRPC(Config &config) {
+bool StartHTTPRPC(Config &config,
+                  HTTPRPCRequestProcessor &httpRPCRequestProcessor) {
     LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
     if (!InitRPCAuthentication(config)) return false;
 
-    RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
+    const std::function<bool(Config &, HTTPRequest *, const std::string &)>
+        &rpcFunction =
+            std::bind(&HTTPRPCRequestProcessor::DelegateHTTPRequest,
+                      &httpRPCRequestProcessor, std::placeholders::_2);
+    RegisterHTTPHandler("/", true, rpcFunction);
 #ifdef ENABLE_WALLET
     // ifdef can be removed once we switch to better endpoint support and API
     // versioning
-    RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
+    RegisterHTTPHandler("/wallet/", false, rpcFunction);
 #endif
     assert(EventBase());
     httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
     RPCSetTimerInterface(httpRPCTimerInterface);
     return true;
 }
 
 void InterruptHTTPRPC() {
     LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
 }
 
 void StopHTTPRPC() {
     LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
     UnregisterHTTPHandler("/", true);
     if (httpRPCTimerInterface) {
         RPCUnsetTimerInterface(httpRPCTimerInterface);
         delete httpRPCTimerInterface;
         httpRPCTimerInterface = 0;
     }
 }
diff --git a/src/httprpc.h b/src/httprpc.h
index 5ae8792bc..07a4099bc 100644
--- a/src/httprpc.h
+++ b/src/httprpc.h
@@ -1,44 +1,63 @@
 // Copyright (c) 2015 The Bitcoin Core developers
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 #ifndef BITCOIN_HTTPRPC_H
 #define BITCOIN_HTTPRPC_H
 
+#include "httpserver.h"
+#include "rpc/server.h"
+
 #include <map>
 #include <string>
 
 class Config;
 class HTTPRequest;
 
+class HTTPRPCRequestProcessor {
+private:
+    Config &config;
+
+    bool ProcessHTTPRequest(HTTPRequest *request);
+
+public:
+    HTTPRPCRequestProcessor(Config &configIn) : config(configIn) {}
+
+    static bool DelegateHTTPRequest(HTTPRPCRequestProcessor *requestProcessor,
+                                    HTTPRequest *request) {
+        return requestProcessor->ProcessHTTPRequest(request);
+    }
+};
+
 /**
  * Start HTTP RPC subsystem.
  * Precondition; HTTP and RPC has been started.
  */
-bool StartHTTPRPC(Config &config);
+bool StartHTTPRPC(Config &config,
+                  HTTPRPCRequestProcessor &httpRPCRequestProcessor);
 
 /** Interrupt HTTP RPC subsystem */
 void InterruptHTTPRPC();
 
 /**
  * Stop HTTP RPC subsystem.
  * Precondition; HTTP and RPC has been stopped.
  */
 void StopHTTPRPC();
 
 /**
  * Start HTTP REST subsystem.
  * Precondition; HTTP and RPC has been started.
  */
 bool StartREST();
 
 /** Interrupt RPC REST subsystem */
 void InterruptREST();
 
 /**
  * Stop HTTP REST subsystem.
  * Precondition; HTTP and RPC has been stopped.
  */
 void StopREST();
 
 #endif
diff --git a/src/init.cpp b/src/init.cpp
index 7c2c4ae24..84bf98764 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,2307 +1,2310 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2016 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 "addrman.h"
 #include "amount.h"
 #include "chain.h"
 #include "chainparams.h"
 #include "checkpoints.h"
 #include "compat/sanity.h"
 #include "config.h"
 #include "consensus/validation.h"
 #include "diskblockpos.h"
 #include "fs.h"
 #include "httprpc.h"
 #include "httpserver.h"
 #include "key.h"
 #include "miner.h"
 #include "net.h"
 #include "net_processing.h"
 #include "netbase.h"
 #include "policy/policy.h"
 #include "rpc/register.h"
 #include "rpc/server.h"
 #include "scheduler.h"
 #include "script/scriptcache.h"
 #include "script/sigcache.h"
 #include "script/standard.h"
 #include "timedata.h"
 #include "torcontrol.h"
 #include "txdb.h"
 #include "txmempool.h"
 #include "ui_interface.h"
 #include "util.h"
 #include "utilmoneystr.h"
 #include "validation.h"
 #include "validationinterface.h"
 #ifdef ENABLE_WALLET
 #include "wallet/rpcdump.h"
 #include "wallet/wallet.h"
 #endif
 #include "warnings.h"
 
 #include <cstdint>
 #include <cstdio>
 #include <memory>
 
 #ifndef WIN32
 #include <signal.h>
 #endif
 
 #include <boost/algorithm/string/classification.hpp>
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/algorithm/string/split.hpp>
 #include <boost/bind.hpp>
 #include <boost/interprocess/sync/file_lock.hpp>
 #include <boost/thread.hpp>
 
 #if ENABLE_ZMQ
 #include "zmq/zmqnotificationinterface.h"
 #endif
 
 bool fFeeEstimatesInitialized = false;
 static const bool DEFAULT_PROXYRANDOMIZE = true;
 static const bool DEFAULT_REST_ENABLE = false;
 static const bool DEFAULT_DISABLE_SAFEMODE = false;
 static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
 
 std::unique_ptr<CConnman> g_connman;
 std::unique_ptr<PeerLogicValidation> peerLogic;
 
 #if ENABLE_ZMQ
 static CZMQNotificationInterface *pzmqNotificationInterface = nullptr;
 #endif
 
 #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 *FEE_ESTIMATES_FILENAME = "fee_estimates.dat";
 
 //////////////////////////////////////////////////////////////////////////////
 //
 // 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 triggers the DetectShutdownThread(), which interrupts
 // the main thread group. DetectShutdownThread() then exits, which causes
 // AppInit() to continue (it .joins the shutdown 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.
 //
 // Note that if running -daemon the parent process returns from AppInit2 before
 // adding any threads to the threadGroup, so .join_all() returns immediately and
 // the parent exits from main().
 //
 // Shutdown for Qt is very similar, only it uses a QTimer to detect
 // fRequestShutdown getting set, and then does the normal Qt shutdown thing.
 //
 
 std::atomic<bool> fRequestShutdown(false);
 std::atomic<bool> fDumpMempoolLater(false);
 
 void StartShutdown() {
     fRequestShutdown = true;
 }
 bool ShutdownRequested() {
     return fRequestShutdown;
 }
 
 /**
  * This is a minimally invasive approach to shutdown on LevelDB read errors from
  * the chainstate, while keeping user interface out of the common library, which
  * is shared between bitcoind, and bitcoin-qt and non-server tools.
  */
 class CCoinsViewErrorCatcher final : public CCoinsViewBacked {
 public:
     CCoinsViewErrorCatcher(CCoinsView *view) : CCoinsViewBacked(view) {}
     bool GetCoin(const COutPoint &outpoint, Coin &coin) const override {
         try {
             return CCoinsViewBacked::GetCoin(outpoint, coin);
         } catch (const std::runtime_error &e) {
             uiInterface.ThreadSafeMessageBox(
                 _("Error reading from database, shutting down."), "",
                 CClientUIInterface::MSG_ERROR);
             LogPrintf("Error reading from database: %s\n", e.what());
             // Starting the shutdown sequence and returning false to the caller
             // would be interpreted as 'entry not found' (as opposed to unable
             // to read data), and could lead to invalid interpretation. Just
             // exit immediately, as we can't continue anyway, and all writes
             // should be atomic.
             abort();
         }
     }
     // Writes do not need similar protection, as failure to write is handled by
     // the caller.
 };
 
 static CCoinsViewDB *pcoinsdbview = nullptr;
 static CCoinsViewErrorCatcher *pcoinscatcher = nullptr;
 static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
 
 void Interrupt(boost::thread_group &threadGroup) {
     InterruptHTTPServer();
     InterruptHTTPRPC();
     InterruptRPC();
     InterruptREST();
     InterruptTorControl();
     InterruptMapPort();
     if (g_connman) {
         g_connman->Interrupt();
     }
     threadGroup.interrupt_all();
 }
 
 void Shutdown() {
     LogPrintf("%s: In progress...\n", __func__);
     static CCriticalSection cs_Shutdown;
     TRY_LOCK(cs_Shutdown, lockShutdown);
     if (!lockShutdown) {
         return;
     }
 
     /// Note: Shutdown() must be able to handle cases in which AppInit2() 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.
     RenameThread("bitcoin-shutoff");
     mempool.AddTransactionsUpdated(1);
 
     StopHTTPRPC();
     StopREST();
     StopRPC();
     StopHTTPServer();
 #ifdef ENABLE_WALLET
     for (CWalletRef pwallet : vpwallets) {
         pwallet->Flush(false);
     }
 #endif
     StopMapPort();
 
     // Because these depend on each-other, we make sure that neither can be
     // using the other before destroying them.
     UnregisterValidationInterface(peerLogic.get());
     g_connman->Stop();
     peerLogic.reset();
     g_connman.reset();
 
     StopTorControl();
     if (fDumpMempoolLater &&
         gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
         DumpMempool();
     }
 
     if (fFeeEstimatesInitialized) {
         fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
         CAutoFile est_fileout(fsbridge::fopen(est_path, "wb"), SER_DISK,
                               CLIENT_VERSION);
         if (!est_fileout.IsNull()) {
             mempool.WriteFeeEstimates(est_fileout);
         } else {
             LogPrintf("%s: Failed to write fee estimates to %s\n", __func__,
                       est_path.string());
         }
         fFeeEstimatesInitialized = false;
     }
 
     {
         LOCK(cs_main);
         if (pcoinsTip != nullptr) {
             FlushStateToDisk();
         }
         delete pcoinsTip;
         pcoinsTip = nullptr;
         delete pcoinscatcher;
         pcoinscatcher = nullptr;
         delete pcoinsdbview;
         pcoinsdbview = nullptr;
         delete pblocktree;
         pblocktree = nullptr;
     }
 #ifdef ENABLE_WALLET
     for (CWalletRef pwallet : vpwallets) {
         pwallet->Flush(true);
     }
 #endif
 
 #if ENABLE_ZMQ
     if (pzmqNotificationInterface) {
         UnregisterValidationInterface(pzmqNotificationInterface);
         delete pzmqNotificationInterface;
         pzmqNotificationInterface = nullptr;
     }
 #endif
 
 #ifndef WIN32
     try {
         fs::remove(GetPidFile());
     } catch (const fs::filesystem_error &e) {
         LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
     }
 #endif
     UnregisterAllValidationInterfaces();
 #ifdef ENABLE_WALLET
     for (CWalletRef pwallet : vpwallets) {
         delete pwallet;
     }
     vpwallets.clear();
 #endif
     globalVerifyHandle.reset();
     ECC_Stop();
     LogPrintf("%s: done\n", __func__);
 }
 
 /**
  * Signal handlers are very limited in what they are allowed to do, so:
  */
 void HandleSIGTERM(int) {
     fRequestShutdown = true;
 }
 
 void HandleSIGHUP(int) {
     GetLogger().fReopenDebugLog = true;
 }
 
 void OnRPCStarted() {
     uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange);
 }
 
 void OnRPCStopped() {
     uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange);
     RPCNotifyBlockChange(false, nullptr);
     cvBlockChange.notify_all();
     LogPrint(BCLog::RPC, "RPC stopped.\n");
 }
 
 void OnRPCPreCommand(const ContextFreeRPCCommand &cmd) {
     // Observe safe mode.
     std::string strWarning = GetWarnings("rpc");
     if (strWarning != "" &&
         !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) &&
         !cmd.okSafeMode) {
         throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE,
                            std::string("Safe mode: ") + strWarning);
     }
 }
 
 std::string HelpMessage(HelpMessageMode mode) {
     const auto defaultBaseParams =
         CreateBaseChainParams(CBaseChainParams::MAIN);
     const auto testnetBaseParams =
         CreateBaseChainParams(CBaseChainParams::TESTNET);
     const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN);
     const auto testnetChainParams =
         CreateChainParams(CBaseChainParams::TESTNET);
     const bool showDebug = gArgs.GetBoolArg("-help-debug", false);
 
     // 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.
     std::string strUsage = HelpMessageGroup(_("Options:"));
     strUsage += HelpMessageOpt("-?", _("Print this help message and exit"));
     strUsage += HelpMessageOpt("-version", _("Print version and exit"));
     strUsage += HelpMessageOpt(
         "-alertnotify=<cmd>",
         _("Execute command when a relevant alert is received or we see a "
           "really long fork (%s in cmd is replaced by message)"));
     strUsage += HelpMessageOpt("-blocknotify=<cmd>",
                                _("Execute command when the best block changes "
                                  "(%s in cmd is replaced by block hash)"));
     if (showDebug) {
         strUsage += HelpMessageOpt(
             "-blocksonly",
             strprintf(
                 _("Whether to operate in a blocks only mode (default: %d)"),
                 DEFAULT_BLOCKSONLY));
     }
     strUsage += HelpMessageOpt(
         "-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()));
     strUsage += HelpMessageOpt(
         "-conf=<file>", strprintf(_("Specify configuration file (default: %s)"),
                                   BITCOIN_CONF_FILENAME));
     if (mode == HMM_BITCOIND) {
 #if HAVE_DECL_DAEMON
         strUsage += HelpMessageOpt(
             "-daemon",
             _("Run in the background as a daemon and accept commands"));
 #endif
     }
     strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
     if (showDebug) {
         strUsage += HelpMessageOpt(
             "-dbbatchsize",
             strprintf(
                 "Maximum database write batch size in bytes (default: %u)",
                 nDefaultDbBatchSize));
     }
     strUsage += HelpMessageOpt(
         "-dbcache=<n>",
         strprintf(
             _("Set database cache size in megabytes (%d to %d, default: %d)"),
             nMinDbCache, nMaxDbCache, nDefaultDbCache));
     if (showDebug) {
         strUsage += HelpMessageOpt(
             "-feefilter", strprintf("Tell other nodes to filter invs to us by "
                                     "our mempool min fee (default: %d)",
                                     DEFAULT_FEEFILTER));
     }
 
     strUsage += HelpMessageOpt(
         "-loadblock=<file>",
         _("Imports blocks from external blk000??.dat file on startup"));
     strUsage += HelpMessageOpt(
         "-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable "
                                         "transactions in memory (default: %u)"),
                                       DEFAULT_MAX_ORPHAN_TRANSACTIONS));
     strUsage += HelpMessageOpt("-maxmempool=<n>",
                                strprintf(_("Keep the transaction memory pool "
                                            "below <n> megabytes (default: %u)"),
                                          DEFAULT_MAX_MEMPOOL_SIZE));
     strUsage +=
         HelpMessageOpt("-mempoolexpiry=<n>",
                        strprintf(_("Do not keep transactions in the mempool "
                                    "longer than <n> hours (default: %u)"),
                                  DEFAULT_MEMPOOL_EXPIRY));
     if (showDebug) {
         strUsage += HelpMessageOpt(
             "-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()));
     }
     strUsage +=
         HelpMessageOpt("-persistmempool",
                        strprintf(_("Whether to save the mempool on shutdown "
                                    "and load on restart (default: %u)"),
                                  DEFAULT_PERSIST_MEMPOOL));
     strUsage += HelpMessageOpt(
         "-blockreconstructionextratxn=<n>",
         strprintf(_("Extra transactions to keep in memory for compact block "
                     "reconstructions (default: %u)"),
                   DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
     strUsage += HelpMessageOpt(
         "-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));
 #ifndef WIN32
     strUsage += HelpMessageOpt(
         "-pid=<file>",
         strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME));
 #endif
     strUsage += HelpMessageOpt(
         "-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 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));
     strUsage += HelpMessageOpt(
         "-reindex-chainstate",
         _("Rebuild chain state from the currently indexed blocks"));
     strUsage +=
         HelpMessageOpt("-reindex", _("Rebuild chain state and block index from "
                                      "the blk*.dat files on disk"));
 #ifndef WIN32
     strUsage += HelpMessageOpt(
         "-sysperms",
         _("Create new files with system default permissions, instead of umask "
           "077 (only effective with disabled wallet functionality)"));
 #endif
     strUsage += HelpMessageOpt(
         "-txindex", strprintf(_("Maintain a full transaction index, used by "
                                 "the getrawtransaction rpc call (default: %d)"),
                               DEFAULT_TXINDEX));
     strUsage += HelpMessageOpt(
         "-usecashaddr", _("Use Cash Address for destination encoding instead "
                           "of base58 (activate by default on Jan, 14)"));
 
     strUsage += HelpMessageGroup(_("Connection options:"));
     strUsage += HelpMessageOpt(
         "-addnode=<ip>",
         _("Add a node to connect to and attempt to keep the connection open "
           "(see the `addnode` RPC command help for more info)"));
     strUsage += HelpMessageOpt(
         "-banscore=<n>",
         strprintf(
             _("Threshold for disconnecting misbehaving peers (default: %u)"),
             DEFAULT_BANSCORE_THRESHOLD));
     strUsage += HelpMessageOpt(
         "-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving "
                                     "peers from reconnecting (default: %u)"),
                                   DEFAULT_MISBEHAVING_BANTIME));
     strUsage += HelpMessageOpt("-bind=<addr>",
                                _("Bind to given address and always listen on "
                                  "it. Use [host]:port notation for IPv6"));
     strUsage += HelpMessageOpt(
         "-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)"));
     strUsage += HelpMessageOpt("-discover",
                                _("Discover own IP addresses (default: 1 when "
                                  "listening and no -externalip or -proxy)"));
     strUsage += HelpMessageOpt(
         "-dns",
         _("Allow DNS lookups for -addnode, -seednode and -connect") + " " +
             strprintf(_("(default: %d)"), DEFAULT_NAME_LOOKUP));
     strUsage += HelpMessageOpt(
         "-dnsseed", _("Query for peer addresses via DNS lookup, if low on "
                       "addresses (default: 1 unless -connect/-noconnect)"));
     strUsage += HelpMessageOpt("-externalip=<ip>",
                                _("Specify your own public address"));
     strUsage += HelpMessageOpt(
         "-forcednsseed",
         strprintf(
             _("Always query for peer addresses via DNS lookup (default: %d)"),
             DEFAULT_FORCEDNSSEED));
     strUsage +=
         HelpMessageOpt("-listen", _("Accept connections from outside (default: "
                                     "1 if no -proxy or -connect/-noconnect)"));
     strUsage += HelpMessageOpt(
         "-listenonion",
         strprintf(_("Automatically create Tor hidden service (default: %d)"),
                   DEFAULT_LISTEN_ONION));
     strUsage += HelpMessageOpt(
         "-maxconnections=<n>",
         strprintf(_("Maintain at most <n> connections to peers (default: %u)"),
                   DEFAULT_MAX_PEER_CONNECTIONS));
     strUsage +=
         HelpMessageOpt("-maxreceivebuffer=<n>",
                        strprintf(_("Maximum per-connection receive buffer, "
                                    "<n>*1000 bytes (default: %u)"),
                                  DEFAULT_MAXRECEIVEBUFFER));
     strUsage += HelpMessageOpt(
         "-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, "
                                           "<n>*1000 bytes (default: %u)"),
                                         DEFAULT_MAXSENDBUFFER));
     strUsage += HelpMessageOpt(
         "-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));
     strUsage +=
         HelpMessageOpt("-onion=<ip:port>",
                        strprintf(_("Use separate SOCKS5 proxy to reach peers "
                                    "via Tor hidden services (default: %s)"),
                                  "-proxy"));
     strUsage += HelpMessageOpt(
         "-onlynet=<net>",
         _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)"));
     strUsage +=
         HelpMessageOpt("-permitbaremultisig",
                        strprintf(_("Relay non-P2SH multisig (default: %d)"),
                                  DEFAULT_PERMIT_BAREMULTISIG));
     strUsage += HelpMessageOpt(
         "-peerbloomfilters",
         strprintf(_("Support filtering of blocks and transaction with bloom "
                     "filters (default: %d)"),
                   DEFAULT_PEERBLOOMFILTERS));
     strUsage += HelpMessageOpt(
         "-port=<port>",
         strprintf(
             _("Listen for connections on <port> (default: %u or testnet: %u)"),
             defaultChainParams->GetDefaultPort(),
             testnetChainParams->GetDefaultPort()));
     strUsage +=
         HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"));
     strUsage += HelpMessageOpt(
         "-proxyrandomize",
         strprintf(_("Randomize credentials for every proxy connection. This "
                     "enables Tor stream isolation (default: %d)"),
                   DEFAULT_PROXYRANDOMIZE));
     strUsage += HelpMessageOpt(
         "-seednode=<ip>",
         _("Connect to a node to retrieve peer addresses, and disconnect"));
     strUsage += HelpMessageOpt(
         "-timeout=<n>", strprintf(_("Specify connection timeout in "
                                     "milliseconds (minimum: 1, default: %d)"),
                                   DEFAULT_CONNECT_TIMEOUT));
     strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>",
                                strprintf(_("Tor control port to use if onion "
                                            "listening enabled (default: %s)"),
                                          DEFAULT_TOR_CONTROL));
     strUsage += HelpMessageOpt("-torpassword=<pass>",
                                _("Tor control port password (default: empty)"));
 #ifdef USE_UPNP
 #if USE_UPNP
     strUsage +=
         HelpMessageOpt("-upnp", _("Use UPnP to map the listening port "
                                   "(default: 1 when listening and no -proxy)"));
 #else
     strUsage += HelpMessageOpt(
         "-upnp",
         strprintf(_("Use UPnP to map the listening port (default: %u)"), 0));
 #endif
 #endif
     strUsage +=
         HelpMessageOpt("-whitebind=<addr>",
                        _("Bind to given address and whitelist peers connecting "
                          "to it. Use [host]:port notation for IPv6"));
     strUsage += HelpMessageOpt(
         "-whitelist=<IP address or network>",
         _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) "
           "or CIDR notated network (e.g. 1.2.3.0/24). Can be specified "
           "multiple times.") +
             " " + _("Whitelisted peers cannot be DoS banned and their "
                     "transactions are always relayed, even if they are already "
                     "in the mempool, useful e.g. for a gateway"));
     strUsage += HelpMessageOpt(
         "-whitelistrelay",
         strprintf(_("Accept relayed transactions received from whitelisted "
                     "peers even when not relaying transactions (default: %d)"),
                   DEFAULT_WHITELISTRELAY));
     strUsage += HelpMessageOpt(
         "-whitelistforcerelay",
         strprintf(_("Force relay of transactions from whitelisted peers even "
                     "if they violate local relay policy (default: %d)"),
                   DEFAULT_WHITELISTFORCERELAY));
     strUsage += HelpMessageOpt(
         "-maxuploadtarget=<n>",
         strprintf(_("Tries to keep outbound traffic under the given target (in "
                     "MiB per 24h), 0 = no limit (default: %d)"),
                   DEFAULT_MAX_UPLOAD_TARGET));
 
 #ifdef ENABLE_WALLET
     strUsage += CWallet::GetWalletHelpString(showDebug);
 #endif
 
 #if ENABLE_ZMQ
     strUsage += HelpMessageGroup(_("ZeroMQ notification options:"));
     strUsage += HelpMessageOpt("-zmqpubhashblock=<address>",
                                _("Enable publish hash block in <address>"));
     strUsage +=
         HelpMessageOpt("-zmqpubhashtx=<address>",
                        _("Enable publish hash transaction in <address>"));
     strUsage += HelpMessageOpt("-zmqpubrawblock=<address>",
                                _("Enable publish raw block in <address>"));
     strUsage +=
         HelpMessageOpt("-zmqpubrawtx=<address>",
                        _("Enable publish raw transaction in <address>"));
 #endif
 
     strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
     strUsage += HelpMessageOpt("-uacomment=<cmt>",
                                _("Append comment to the user agent string"));
     if (showDebug) {
         strUsage += HelpMessageOpt(
             "-checkblocks=<n>",
             strprintf(
                 _("How many blocks to check at startup (default: %u, 0 = all)"),
                 DEFAULT_CHECKBLOCKS));
         strUsage +=
             HelpMessageOpt("-checklevel=<n>",
                            strprintf(_("How thorough the block verification of "
                                        "-checkblocks is (0-4, default: %u)"),
                                      DEFAULT_CHECKLEVEL));
         strUsage += HelpMessageOpt(
             "-checkblockindex",
             strprintf("Do a full consistency check for mapBlockIndex, "
                       "setBlockIndexCandidates, chainActive and "
                       "mapBlocksUnlinked occasionally. Also sets -checkmempool "
                       "(default: %u)",
                       defaultChainParams->DefaultConsistencyChecks()));
         strUsage += HelpMessageOpt(
             "-checkmempool=<n>",
             strprintf("Run checks every <n> transactions (default: %u)",
                       defaultChainParams->DefaultConsistencyChecks()));
         strUsage += HelpMessageOpt(
             "-checkpoints", strprintf("Only accept block chain matching "
                                       "built-in checkpoints (default: %d)",
                                       DEFAULT_CHECKPOINTS_ENABLED));
         strUsage += HelpMessageOpt(
             "-disablesafemode", strprintf("Disable safemode, override a real "
                                           "safe mode event (default: %d)",
                                           DEFAULT_DISABLE_SAFEMODE));
         strUsage += HelpMessageOpt(
             "-testsafemode",
             strprintf("Force safe mode (default: %d)", DEFAULT_TESTSAFEMODE));
         strUsage +=
             HelpMessageOpt("-dropmessagestest=<n>",
                            "Randomly drop 1 of every <n> network messages");
         strUsage +=
             HelpMessageOpt("-fuzzmessagestest=<n>",
                            "Randomly fuzz 1 of every <n> network messages");
         strUsage += HelpMessageOpt(
             "-stopafterblockimport",
             strprintf(
                 "Stop running after importing blocks from disk (default: %d)",
                 DEFAULT_STOPAFTERBLOCKIMPORT));
         strUsage += HelpMessageOpt(
             "-stopatheight", strprintf("Stop running after reaching the given "
                                        "height in the main chain (default: %u)",
                                        DEFAULT_STOPATHEIGHT));
         strUsage += HelpMessageOpt(
             "-limitancestorcount=<n>",
             strprintf("Do not accept transactions if number of in-mempool "
                       "ancestors is <n> or more (default: %u)",
                       DEFAULT_ANCESTOR_LIMIT));
         strUsage +=
             HelpMessageOpt("-limitancestorsize=<n>",
                            strprintf("Do not accept transactions whose size "
                                      "with all in-mempool ancestors exceeds "
                                      "<n> kilobytes (default: %u)",
                                      DEFAULT_ANCESTOR_SIZE_LIMIT));
         strUsage += HelpMessageOpt(
             "-limitdescendantcount=<n>",
             strprintf("Do not accept transactions if any ancestor would have "
                       "<n> or more in-mempool descendants (default: %u)",
                       DEFAULT_DESCENDANT_LIMIT));
         strUsage += HelpMessageOpt(
             "-limitdescendantsize=<n>",
             strprintf("Do not accept transactions if any ancestor would have "
                       "more than <n> kilobytes of in-mempool descendants "
                       "(default: %u).",
                       DEFAULT_DESCENDANT_SIZE_LIMIT));
     }
     strUsage += HelpMessageOpt(
         "-debug=<category>",
         strprintf(_("Output debugging information (default: %u, supplying "
                     "<category> is optional)"),
                   0) +
             ". " + _("If <category> is not supplied or if <category> = 1, "
                      "output all debugging information.") +
             _("<category> can be:") + " " + ListLogCategories() + ".");
     strUsage += HelpMessageOpt(
         "-debugexclude=<category>",
         strprintf(_("Exclude debugging information for a category. Can be used "
                     "in conjunction with -debug=1 to output debug logs for all "
                     "categories except one or more specified categories.")));
     if (showDebug) {
         strUsage += HelpMessageOpt(
             "-nodebug", "Turn off debugging messages, same as -debug=0");
     }
     strUsage += HelpMessageOpt(
         "-help-debug",
         _("Show all debugging options (usage: --help -help-debug)"));
     strUsage += HelpMessageOpt(
         "-logips",
         strprintf(_("Include IP addresses in debug output (default: %d)"),
                   DEFAULT_LOGIPS));
     strUsage += HelpMessageOpt(
         "-logtimestamps",
         strprintf(_("Prepend debug output with timestamp (default: %d)"),
                   DEFAULT_LOGTIMESTAMPS));
     if (showDebug) {
         strUsage += HelpMessageOpt(
             "-logtimemicros",
             strprintf(
                 "Add microsecond precision to debug timestamps (default: %d)",
                 DEFAULT_LOGTIMEMICROS));
         strUsage += HelpMessageOpt(
             "-mocktime=<n>",
             "Replace actual time with <n> seconds since epoch (default: 0)");
         strUsage += HelpMessageOpt(
             "-limitfreerelay=<n>",
             strprintf("Continuously rate-limit free transactions to <n>*1000 "
                       "bytes per minute (default: %u)",
                       DEFAULT_LIMITFREERELAY));
         strUsage +=
             HelpMessageOpt("-relaypriority",
                            strprintf("Require high priority for relaying free "
                                      "or low-fee transactions (default: %d)",
                                      DEFAULT_RELAYPRIORITY));
         strUsage += HelpMessageOpt(
             "-maxsigcachesize=<n>",
             strprintf("Limit size of signature cache to <n> MiB (default: %u)",
                       DEFAULT_MAX_SIG_CACHE_SIZE));
         strUsage += HelpMessageOpt(
             "-maxscriptcachesize=<n>",
             strprintf("Limit size of script cache to <n> MiB (default: %u)",
                       DEFAULT_MAX_SCRIPT_CACHE_SIZE));
         strUsage += HelpMessageOpt(
             "-maxtipage=<n>",
             strprintf("Maximum tip age in seconds to consider node in initial "
                       "block download (default: %u)",
                       DEFAULT_MAX_TIP_AGE));
     }
     strUsage += HelpMessageOpt(
         "-excessutxocharge=<amt>",
         strprintf(_("Fees (in %s/kB) to charge per utxo created for"
                     "relaying, and mining (default: %s)"),
                   CURRENCY_UNIT, FormatMoney(DEFAULT_UTXO_FEE)));
     strUsage += HelpMessageOpt(
         "-minrelaytxfee=<amt>",
         strprintf(
             _("Fees (in %s/kB) smaller than this are considered zero fee for "
               "relaying, mining and transaction creation (default: %s)"),
             CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE_PER_KB)));
     strUsage += HelpMessageOpt(
         "-maxtxfee=<amt>",
         strprintf(_("Maximum total fees (in %s) to use in a single wallet "
                     "transaction or raw transaction; setting this too low may "
                     "abort large transactions (default: %s)"),
                   CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)));
     strUsage += HelpMessageOpt(
         "-printtoconsole",
         _("Send trace/debug info to console instead of debug.log file"));
     if (showDebug) {
         strUsage += HelpMessageOpt(
             "-printpriority", strprintf("Log transaction priority and fee per "
                                         "kB when mining blocks (default: %d)",
                                         DEFAULT_PRINTPRIORITY));
     }
     strUsage += HelpMessageOpt("-shrinkdebugfile",
                                _("Shrink debug.log file on client startup "
                                  "(default: 1 when no -debug)"));
 
     AppendParamsHelpMessages(strUsage, showDebug);
 
     strUsage += HelpMessageGroup(_("Node relay options:"));
     if (showDebug) {
         strUsage += HelpMessageOpt(
             "-acceptnonstdtxn",
             strprintf(
                 "Relay and mine \"non-standard\" transactions (%sdefault: %u)",
                 "testnet/regtest only; ",
                 defaultChainParams->RequireStandard()));
         strUsage +=
             HelpMessageOpt("-excessiveblocksize=<n>",
                            strprintf(_("Do not accept blocks larger than this "
                                        "limit, in bytes (default: %d)"),
                                      DEFAULT_MAX_BLOCK_SIZE));
         strUsage += HelpMessageOpt(
             "-dustrelayfee=<amt>",
             strprintf("Fee rate (in %s/kB) used to defined 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)",
                       CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)));
     }
     strUsage +=
         HelpMessageOpt("-bytespersigop",
                        strprintf(_("Equivalent bytes per sigop in transactions "
                                    "for relay and mining (default: %u)"),
                                  DEFAULT_BYTES_PER_SIGOP));
     strUsage += HelpMessageOpt(
         "-datacarrier",
         strprintf(_("Relay and mine data carrier transactions (default: %d)"),
                   DEFAULT_ACCEPT_DATACARRIER));
     strUsage += HelpMessageOpt(
         "-datacarriersize",
         strprintf(_("Maximum size of data in data carrier transactions we "
                     "relay and mine (default: %u)"),
                   MAX_OP_RETURN_RELAY));
 
     strUsage += HelpMessageGroup(_("Block creation options:"));
     strUsage += HelpMessageOpt(
         "-blockmaxsize=<n>",
         strprintf(_("Set maximum block size in bytes (default: %d)"),
                   DEFAULT_MAX_GENERATED_BLOCK_SIZE));
     strUsage += HelpMessageOpt(
         "-blockprioritypercentage=<n>",
         strprintf(_("Set maximum percentage of a block reserved to "
                     "high-priority/low-fee transactions (default: %d)"),
                   DEFAULT_BLOCK_PRIORITY_PERCENTAGE));
     strUsage += HelpMessageOpt(
         "-blockmintxfee=<amt>",
         strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be "
                     "included in block creation. (default: %s)"),
                   CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB)));
     if (showDebug) {
         strUsage +=
             HelpMessageOpt("-blockversion=<n>",
                            "Override block version to test forking scenarios");
     }
 
     strUsage += HelpMessageGroup(_("RPC server options:"));
     strUsage += HelpMessageOpt("-server",
                                _("Accept command line and JSON-RPC commands"));
     strUsage += HelpMessageOpt(
         "-rest", strprintf(_("Accept public REST requests (default: %d)"),
                            DEFAULT_REST_ENABLE));
     strUsage += HelpMessageOpt(
         "-rpcbind=<addr>",
         _("Bind to given address to listen for JSON-RPC connections. Use "
           "[host]:port notation for IPv6. This option can be specified "
           "multiple times (default: bind to all interfaces)"));
     strUsage +=
         HelpMessageOpt("-rpccookiefile=<loc>",
                        _("Location of the auth cookie (default: data dir)"));
     strUsage += HelpMessageOpt("-rpcuser=<user>",
                                _("Username for JSON-RPC connections"));
     strUsage += HelpMessageOpt("-rpcpassword=<pw>",
                                _("Password for JSON-RPC connections"));
     strUsage += HelpMessageOpt(
         "-rpcauth=<userpw>",
         _("Username and hashed password for JSON-RPC connections. The field "
           "<userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical "
           "python script is included in share/rpcuser. The client then "
           "connects normally using the "
           "rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This "
           "option can be specified multiple times"));
     strUsage += HelpMessageOpt(
         "-rpcport=<port>",
         strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or "
                     "testnet: %u)"),
                   defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()));
     strUsage += HelpMessageOpt(
         "-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"));
     strUsage += HelpMessageOpt(
         "-rpcthreads=<n>",
         strprintf(
             _("Set the number of threads to service RPC calls (default: %d)"),
             DEFAULT_HTTP_THREADS));
     strUsage += HelpMessageOpt(
         "-rpccorsdomain=value",
         "Domain from which to accept cross origin requests (browser enforced)");
     if (showDebug) {
         strUsage += HelpMessageOpt(
             "-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to "
                                            "service RPC calls (default: %d)",
                                            DEFAULT_HTTP_WORKQUEUE));
         strUsage += HelpMessageOpt(
             "-rpcservertimeout=<n>",
             strprintf("Timeout during HTTP requests (default: %d)",
                       DEFAULT_HTTP_SERVER_TIMEOUT));
     }
 
     return strUsage;
 }
 
 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"), 2009, COPYRIGHT_YEAR) +
                " ") +
            "\n" + "\n" +
            strprintf(_("Please contribute if you find %s useful. "
                        "Visit %s for further information about the software."),
                      PACKAGE_NAME, URL_WEBSITE) +
            "\n" + strprintf(_("The source code is available from %s."),
                             URL_SOURCE_CODE) +
            "\n" + "\n" + _("This is experimental software.") + "\n" +
            strprintf(_("Distributed under the MIT software license, see the "
                        "accompanying file %s or %s"),
                      "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."),
                      "<https://www.openssl.org>") +
            "\n";
 }
 
 static void BlockNotifyCallback(bool initialSync,
                                 const CBlockIndex *pBlockIndex) {
     if (initialSync || !pBlockIndex) {
         return;
     }
 
     std::string strCmd = gArgs.GetArg("-blocknotify", "");
     if (!strCmd.empty()) {
         boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex());
         std::thread t(runCommand, strCmd);
         // thread runs free
         t.detach();
     }
 }
 
 static bool fHaveGenesis = false;
 static boost::mutex cs_GenesisWait;
 static CConditionVariable condvar_GenesisWait;
 
 static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) {
     if (pBlockIndex != nullptr) {
         {
             boost::unique_lock<boost::mutex> lock_GenesisWait(cs_GenesisWait);
             fHaveGenesis = true;
         }
         condvar_GenesisWait.notify_all();
     }
 }
 
 struct CImportingNow {
     CImportingNow() {
         assert(fImporting == false);
         fImporting = true;
     }
 
     ~CImportingNow() {
         assert(fImporting == true);
         fImporting = false;
     }
 };
 
 // If we're using -prune with -reindex, then delete block files that will be
 // ignored by the reindex.  Since reindexing works by starting at block file 0
 // and looping until a blockfile is missing, do the same here to delete any
 // later block files after a gap. Also delete all rev files since they'll be
 // rewritten by the reindex anyway. This ensures that vinfoBlockFile is in sync
 // with what's actually on disk by the time we start downloading, so that
 // pruning works correctly.
 void CleanupBlockRevFiles() {
     std::map<std::string, fs::path> mapBlockFiles;
 
     // Glob all blk?????.dat and rev?????.dat files from the blocks directory.
     // Remove the rev files immediately and insert the blk file paths into an
     // ordered map keyed by block file index.
     LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for "
               "-reindex with -prune\n");
     fs::path blocksdir = GetDataDir() / "blocks";
     for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator();
          it++) {
         if (is_regular_file(*it) &&
             it->path().filename().string().length() == 12 &&
             it->path().filename().string().substr(8, 4) == ".dat") {
             if (it->path().filename().string().substr(0, 3) == "blk") {
                 mapBlockFiles[it->path().filename().string().substr(3, 5)] =
                     it->path();
             } else if (it->path().filename().string().substr(0, 3) == "rev") {
                 remove(it->path());
             }
         }
     }
 
     // Remove all block files that aren't part of a contiguous set starting at
     // zero by walking the ordered map (keys are block file indices) by keeping
     // a separate counter. Once we hit a gap (or if 0 doesn't exist) start
     // removing block files.
     int nContigCounter = 0;
     for (const std::pair<std::string, fs::path> &item : mapBlockFiles) {
         if (atoi(item.first) == nContigCounter) {
             nContigCounter++;
             continue;
         }
         remove(item.second);
     }
 }
 
 void ThreadImport(const Config &config, std::vector<fs::path> vImportFiles) {
     RenameThread("bitcoin-loadblk");
 
     {
         CImportingNow imp;
 
         // -reindex
         if (fReindex) {
             int nFile = 0;
             while (true) {
                 CDiskBlockPos pos(nFile, 0);
                 if (!fs::exists(GetBlockPosFilename(pos, "blk"))) {
                     // No block files left to reindex
                     break;
                 }
                 FILE *file = OpenBlockFile(pos, true);
                 if (!file) {
                     // This error is logged in OpenBlockFile
                     break;
                 }
                 LogPrintf("Reindexing block file blk%05u.dat...\n",
                           (unsigned int)nFile);
                 LoadExternalBlockFile(config, file, &pos);
                 nFile++;
             }
             pblocktree->WriteReindexing(false);
             fReindex = false;
             LogPrintf("Reindexing finished\n");
             // To avoid ending up in a situation without genesis block, re-try
             // initializing (no-op if reindexing worked):
             InitBlockIndex(config);
         }
 
         // hardcoded $DATADIR/bootstrap.dat
         fs::path pathBootstrap = GetDataDir() / "bootstrap.dat";
         if (fs::exists(pathBootstrap)) {
             FILE *file = fsbridge::fopen(pathBootstrap, "rb");
             if (file) {
                 fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
                 LogPrintf("Importing bootstrap.dat...\n");
                 LoadExternalBlockFile(config, file);
                 RenameOver(pathBootstrap, pathBootstrapOld);
             } else {
                 LogPrintf("Warning: Could not open bootstrap file %s\n",
                           pathBootstrap.string());
             }
         }
 
         // -loadblock=
         for (const fs::path &path : vImportFiles) {
             FILE *file = fsbridge::fopen(path, "rb");
             if (file) {
                 LogPrintf("Importing blocks file %s...\n", path.string());
                 LoadExternalBlockFile(config, file);
             } else {
                 LogPrintf("Warning: Could not open blocks file %s\n",
                           path.string());
             }
         }
 
         // scan for better chains in the block chain database, that are not yet
         // connected in the active best chain
         CValidationState state;
         if (!ActivateBestChain(config, state)) {
             LogPrintf("Failed to connect best block");
             StartShutdown();
         }
 
         if (gArgs.GetBoolArg("-stopafterblockimport",
                              DEFAULT_STOPAFTERBLOCKIMPORT)) {
             LogPrintf("Stopping after block import\n");
             StartShutdown();
         }
     } // End scope of CImportingNow
     if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
         LoadMempool(config);
         fDumpMempoolLater = !fRequestShutdown;
     }
 }
 
 /** Sanity checks
  *  Ensure that Bitcoin is running in a usable environment with all
  *  necessary library support.
  */
 bool InitSanityCheck(void) {
     if (!ECC_InitSanityCheck()) {
         InitError(
             "Elliptic curve cryptography sanity check failure. Aborting.");
         return false;
     }
 
     if (!glibc_sanity_test() || !glibcxx_sanity_test()) {
         return false;
     }
 
     if (!Random_SanityCheck()) {
         InitError("OS cryptographic RNG sanity check failure. Aborting.");
         return false;
     }
 
     return true;
 }
 
-static bool AppInitServers(Config &config, boost::thread_group &threadGroup) {
+static bool AppInitServers(Config &config,
+                           HTTPRPCRequestProcessor &httpRPCRequestProcessor,
+                           boost::thread_group &threadGroup) {
     RPCServerSignals::OnStarted(&OnRPCStarted);
     RPCServerSignals::OnStopped(&OnRPCStopped);
     RPCServerSignals::OnPreCommand(&OnRPCPreCommand);
     if (!InitHTTPServer(config)) {
         return false;
     }
     if (!StartRPC()) {
         return false;
     }
-    if (!StartHTTPRPC(config)) {
+    if (!StartHTTPRPC(config, httpRPCRequestProcessor)) {
         return false;
     }
     if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST()) {
         return false;
     }
     if (!StartHTTPServer()) {
         return false;
     }
     return true;
 }
 
 // Parameter interaction based on rules
 void InitParameterInteraction() {
     // when specifying an explicit binding address, you want to listen on it
     // even when -connect or -proxy is specified.
     if (gArgs.IsArgSet("-bind")) {
         if (gArgs.SoftSetBoolArg("-listen", true)) {
             LogPrintf(
                 "%s: parameter interaction: -bind set -> setting -listen=1\n",
                 __func__);
         }
     }
     if (gArgs.IsArgSet("-whitebind")) {
         if (gArgs.SoftSetBoolArg("-listen", true)) {
             LogPrintf("%s: parameter interaction: -whitebind set -> setting "
                       "-listen=1\n",
                       __func__);
         }
     }
 
     if (gArgs.IsArgSet("-connect")) {
         // when only connecting to trusted nodes, do not seed via DNS, or listen
         // by default.
         if (gArgs.SoftSetBoolArg("-dnsseed", false)) {
             LogPrintf("%s: parameter interaction: -connect set -> setting "
                       "-dnsseed=0\n",
                       __func__);
         }
         if (gArgs.SoftSetBoolArg("-listen", false)) {
             LogPrintf("%s: parameter interaction: -connect set -> setting "
                       "-listen=0\n",
                       __func__);
         }
     }
 
     if (gArgs.IsArgSet("-proxy")) {
         // to protect privacy, do not listen by default if a default proxy
         // server is specified.
         if (gArgs.SoftSetBoolArg("-listen", false)) {
             LogPrintf(
                 "%s: parameter interaction: -proxy set -> setting -listen=0\n",
                 __func__);
         }
         // to protect privacy, do not use UPNP 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 (gArgs.SoftSetBoolArg("-upnp", false)) {
             LogPrintf(
                 "%s: parameter interaction: -proxy set -> setting -upnp=0\n",
                 __func__);
         }
         // to protect privacy, do not discover addresses by default
         if (gArgs.SoftSetBoolArg("-discover", false)) {
             LogPrintf("%s: parameter interaction: -proxy set -> setting "
                       "-discover=0\n",
                       __func__);
         }
     }
 
     if (!gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
         // do not map ports or try to retrieve public IP when not listening
         // (pointless)
         if (gArgs.SoftSetBoolArg("-upnp", false)) {
             LogPrintf(
                 "%s: parameter interaction: -listen=0 -> setting -upnp=0\n",
                 __func__);
         }
         if (gArgs.SoftSetBoolArg("-discover", false)) {
             LogPrintf(
                 "%s: parameter interaction: -listen=0 -> setting -discover=0\n",
                 __func__);
         }
         if (gArgs.SoftSetBoolArg("-listenonion", false)) {
             LogPrintf("%s: parameter interaction: -listen=0 -> setting "
                       "-listenonion=0\n",
                       __func__);
         }
     }
 
     if (gArgs.IsArgSet("-externalip")) {
         // if an explicit public IP is specified, do not try to find others
         if (gArgs.SoftSetBoolArg("-discover", false)) {
             LogPrintf("%s: parameter interaction: -externalip set -> setting "
                       "-discover=0\n",
                       __func__);
         }
     }
 
     // disable whitelistrelay in blocksonly mode
     if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
         if (gArgs.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 (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
         if (gArgs.SoftSetBoolArg("-whitelistrelay", true)) {
             LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> "
                       "setting -whitelistrelay=1\n",
                       __func__);
         }
     }
 }
 
 static std::string ResolveErrMsg(const char *const optname,
                                  const std::string &strBind) {
     return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
 }
 
 void InitLogging() {
     BCLog::Logger &logger = GetLogger();
     logger.fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false);
     logger.fLogTimestamps =
         gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
     logger.fLogTimeMicros =
         gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
 
     fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
 
     LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
     LogPrintf("%s version %s\n", CLIENT_NAME, FormatFullVersion());
 }
 
 namespace { // Variables internal to initialization process only
 
 int nMaxConnections;
 int nUserMaxConnections;
 int nFD;
 ServiceFlags nLocalServices = NODE_NETWORK;
 } // 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() {
 // 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));
 #endif
 #if _MSC_VER >= 1400
     // 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)
 // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008
 // A failure is non-critical and needs no further attention!
 #ifndef PROCESS_DEP_ENABLE
 // We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >=
 // 0x0601 (Windows 7), which is not correct. Can be removed, when GCCs winbase.h
 // is fixed!
 #define PROCESS_DEP_ENABLE 0x00000001
 #endif
     typedef BOOL(WINAPI * PSETPROCDEPPOL)(DWORD);
     PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(
         GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy");
     if (setProcDEPPol != nullptr) {
         setProcDEPPol(PROCESS_DEP_ENABLE);
     }
 #endif
 
     if (!SetupNetworking()) {
         return InitError("Initializing networking failed");
     }
 
 #ifndef WIN32
     if (!gArgs.GetBoolArg("-sysperms", false)) {
         umask(077);
     }
 
     // Clean shutdown on SIGTERM
     struct sigaction sa;
     sa.sa_handler = HandleSIGTERM;
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = 0;
     sigaction(SIGTERM, &sa, nullptr);
     sigaction(SIGINT, &sa, nullptr);
 
     // Reopen debug.log on SIGHUP
     struct sigaction sa_hup;
     sa_hup.sa_handler = HandleSIGHUP;
     sigemptyset(&sa_hup.sa_mask);
     sa_hup.sa_flags = 0;
     sigaction(SIGHUP, &sa_hup, nullptr);
 
     // Ignore SIGPIPE, otherwise it will bring the daemon down if the client
     // closes unexpectedly
     signal(SIGPIPE, SIG_IGN);
 #endif
 
     std::set_new_handler(new_handler_terminate);
 
     return true;
 }
 
 bool AppInitParameterInteraction(Config &config) {
     const CChainParams &chainparams = config.GetChainParams();
     // Step 2: parameter interactions
 
     // also see: InitParameterInteraction()
 
     // if using block pruning, then disallow txindex
     if (gArgs.GetArg("-prune", 0)) {
         if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
             return InitError(_("Prune mode is incompatible with -txindex."));
         }
     }
 
     // if space reserved for high priority transactions is misconfigured
     // stop program execution and warn the user with a proper error message
     const int64_t blkprio = gArgs.GetArg("-blockprioritypercentage",
                                          DEFAULT_BLOCK_PRIORITY_PERCENTAGE);
     if (!config.SetBlockPriorityPercentage(blkprio)) {
         return InitError(_("Block priority percentage has to belong to the "
                            "[0..100] interval."));
     }
 
     // -bind and -whitebind can't be set when not listening
     size_t nUserBind =
         gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size();
     if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
         return InitError(
             "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 =
         gArgs.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
     nMaxConnections = std::max(nUserMaxConnections, 0);
 
     // Trim requested connection counts, to fit into system limitations
     nMaxConnections =
         std::max(std::min(nMaxConnections,
                           (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS -
                                 MAX_ADDNODE_CONNECTIONS)),
                  0);
     nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS +
                                    MAX_ADDNODE_CONNECTIONS);
     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) {
         InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, "
                                 "because of system limitations."),
                               nUserMaxConnections, nMaxConnections));
     }
 
     // Step 3: parameter-to-internal-flags
     if (gArgs.IsArgSet("-debug")) {
         // Special-case: if -debug=0/-nodebug is set, turn off debugging
         // messages
         const std::vector<std::string> &categories = gArgs.GetArgs("-debug");
         if (find(categories.begin(), categories.end(), std::string("0")) ==
             categories.end()) {
             for (const auto &cat : categories) {
                 BCLog::LogFlags flag;
                 if (!GetLogCategory(flag, cat)) {
                     InitWarning(
                         strprintf(_("Unsupported logging category %s=%s."),
                                   "-debug", cat));
                 }
                 GetLogger().EnableCategory(flag);
             }
         }
     }
 
     // Now remove the logging categories which were explicitly excluded
     for (const std::string &cat : gArgs.GetArgs("-debugexclude")) {
         BCLog::LogFlags flag;
         if (!GetLogCategory(flag, cat)) {
             InitWarning(strprintf(_("Unsupported logging category %s=%s."),
                                   "-debugexclude", cat));
         }
         GetLogger().DisableCategory(flag);
     }
 
     // Check for -debugnet
     if (gArgs.GetBoolArg("-debugnet", false)) {
         InitWarning(
             _("Unsupported argument -debugnet ignored, use -debug=net."));
     }
     // Check for -socks - as this is a privacy risk to continue, exit here
     if (gArgs.IsArgSet("-socks")) {
         return InitError(
             _("Unsupported argument -socks found. Setting SOCKS version isn't "
               "possible anymore, only SOCKS5 proxies are supported."));
     }
     // Check for -tor - as this is a privacy risk to continue, exit here
     if (gArgs.GetBoolArg("-tor", false)) {
         return InitError(_("Unsupported argument -tor found, use -onion."));
     }
 
     if (gArgs.GetBoolArg("-benchmark", false)) {
         InitWarning(
             _("Unsupported argument -benchmark ignored, use -debug=bench."));
     }
 
     if (gArgs.GetBoolArg("-whitelistalwaysrelay", false)) {
         InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use "
                       "-whitelistrelay and/or -whitelistforcerelay."));
     }
 
     if (gArgs.IsArgSet("-blockminsize")) {
         InitWarning("Unsupported argument -blockminsize ignored.");
     }
 
     // Checkmempool and checkblockindex default to true in regtest mode
     int ratio = std::min<int>(
         std::max<int>(
             gArgs.GetArg("-checkmempool",
                          chainparams.DefaultConsistencyChecks() ? 1 : 0),
             0),
         1000000);
     if (ratio != 0) {
         mempool.setSanityCheck(1.0 / ratio);
     }
     fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex",
                                         chainparams.DefaultConsistencyChecks());
     fCheckpointsEnabled =
         gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
 
     hashAssumeValid = uint256S(
         gArgs.GetArg("-assumevalid",
                      chainparams.GetConsensus().defaultAssumeValid.GetHex()));
     if (!hashAssumeValid.IsNull()) {
         LogPrintf("Assuming ancestors of block %s have valid signatures.\n",
                   hashAssumeValid.GetHex());
     } else {
         LogPrintf("Validating signatures for all blocks.\n");
     }
 
     if (gArgs.IsArgSet("-minimumchainwork")) {
         const std::string minChainWorkStr =
             gArgs.GetArg("-minimumchainwork", "");
         if (!IsHexNumber(minChainWorkStr)) {
             return InitError(strprintf(
                 "Invalid non-hex (%s) minimum chain work value specified",
                 minChainWorkStr));
         }
         nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr));
     } else {
         nMinimumChainWork =
             UintToArith256(chainparams.GetConsensus().nMinimumChainWork);
     }
     LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex());
     if (nMinimumChainWork <
         UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) {
         LogPrintf("Warning: nMinimumChainWork set below default value of %s\n",
                   chainparams.GetConsensus().nMinimumChainWork.GetHex());
     }
 
     // mempool limits
     int64_t nMempoolSizeMax =
         gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
     int64_t nMempoolSizeMin =
         gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) *
         1000 * 40;
     if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin) {
         return InitError(strprintf(_("-maxmempool must be at least %d MB"),
                                    std::ceil(nMempoolSizeMin / 1000000.0)));
     }
 
     // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
     nScriptCheckThreads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
     if (nScriptCheckThreads <= 0) {
         nScriptCheckThreads += GetNumCores();
     }
     if (nScriptCheckThreads <= 1) {
         nScriptCheckThreads = 0;
     } else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) {
         nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;
     }
 
     // Configure excessive block size.
     const uint64_t nProposedExcessiveBlockSize =
         gArgs.GetArg("-excessiveblocksize", DEFAULT_MAX_BLOCK_SIZE);
     if (!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 uint64_t nProposedMaxGeneratedBlockSize =
         gArgs.GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE);
     if (nProposedMaxGeneratedBlockSize > config.GetMaxBlockSize()) {
         auto msg = _("Max generated block size (blockmaxsize) cannot exceed "
                      "the excessive block size (excessiveblocksize)");
         return InitError(msg);
     }
 
     // block pruning; get the amount of disk space (in MiB) to allot for block &
     // undo files
     int64_t nPruneArg = gArgs.GetArg("-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
         LogPrintf("Block pruning enabled.  Use RPC call "
                   "pruneblockchain(height) to manually prune block and undo "
                   "files.\n");
         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));
         }
         LogPrintf("Prune configured to target %uMiB on disk for block and undo "
                   "files.\n",
                   nPruneTarget / 1024 / 1024);
         fPruneMode = true;
     }
 
     RegisterAllRPCCommands(tableRPC);
 #ifdef ENABLE_WALLET
     RegisterWalletRPCCommands(tableRPC);
     RegisterDumpRPCCommands(tableRPC);
 #endif
 
     nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
     if (nConnectTimeout <= 0) {
         nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
     }
 
     // Obtain the amount to charge excess UTXO
     if (gArgs.IsArgSet("-excessutxocharge")) {
         Amount n = Amount::zero();
         auto parsed = ParseMoney(gArgs.GetArg("-excessutxocharge", ""), n);
         if (!parsed || Amount::zero() > n) {
             return InitError(AmountErrMsg(
                 "excessutxocharge", gArgs.GetArg("-excessutxocharge", "")));
         }
         config.SetExcessUTXOCharge(n);
     } else {
         config.SetExcessUTXOCharge(DEFAULT_UTXO_FEE);
     }
 
     // Fee-per-kilobyte amount considered the same as "free". If you are mining,
     // be careful setting this: if you set it to zero then a transaction spammer
     // can cheaply fill blocks using 1-satoshi-fee transactions. It should be
     // set above the real cost to you of processing a transaction.
     if (gArgs.IsArgSet("-minrelaytxfee")) {
         Amount n = Amount::zero();
         auto parsed = ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n);
         if (!parsed || Amount::zero() == n) {
             return InitError(AmountErrMsg("minrelaytxfee",
                                           gArgs.GetArg("-minrelaytxfee", "")));
         }
         // High fee check is done afterward in CWallet::ParameterInteraction()
         config.SetMinFeePerKB(CFeeRate(n));
     } else {
         config.SetMinFeePerKB(CFeeRate(DEFAULT_MIN_RELAY_TX_FEE_PER_KB));
     }
 
     // Sanity check argument for min fee for including tx in block
     // TODO: Harmonize which arguments need sanity checking and where that
     // happens.
     if (gArgs.IsArgSet("-blockmintxfee")) {
         Amount n = Amount::zero();
         if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) {
             return InitError(AmountErrMsg("blockmintxfee",
                                           gArgs.GetArg("-blockmintxfee", "")));
         }
     }
 
     // Feerate used to define dust.  Shouldn't be changed lightly as old
     // implementations may inadvertently create non-standard transactions.
     if (gArgs.IsArgSet("-dustrelayfee")) {
         Amount n = Amount::zero();
         auto parsed = ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n);
         if (!parsed || Amount::zero() == n) {
             return InitError(AmountErrMsg("dustrelayfee",
                                           gArgs.GetArg("-dustrelayfee", "")));
         }
         dustRelayFee = CFeeRate(n);
     }
 
     fRequireStandard =
         !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
     if (chainparams.RequireStandard() && !fRequireStandard) {
         return InitError(
             strprintf("acceptnonstdtxn is not currently supported for %s chain",
                       chainparams.NetworkIDString()));
     }
     nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);
 
 #ifdef ENABLE_WALLET
     if (!CWallet::ParameterInteraction()) {
         return false;
     }
 #endif
 
     fIsBareMultisigStd =
         gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
     fAcceptDatacarrier =
         gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
 
     // Option to startup with mocktime set (used for regression testing):
     SetMockTime(gArgs.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
 
     if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) {
         nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
     }
 
     // Signal Bitcoin Cash support.
     // TODO: remove some time after the hardfork when no longer needed
     // to differentiate the network nodes.
     nLocalServices = ServiceFlags(nLocalServices | NODE_BITCOIN_CASH);
 
     nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
 
     return true;
 }
 
 static bool LockDataDirectory(bool probeOnly) {
     std::string strDataDir = GetDataDir().string();
 
     // Make sure only a single Bitcoin process is using the data directory.
     fs::path pathLockFile = GetDataDir() / ".lock";
     // empty lock file; created if it doesn't exist.
     FILE *file = fsbridge::fopen(pathLockFile, "a");
     if (file) {
         fclose(file);
     }
 
     try {
         static boost::interprocess::file_lock lock(
             pathLockFile.string().c_str());
         if (!lock.try_lock()) {
             return InitError(
                 strprintf(_("Cannot obtain a lock on data directory %s. %s is "
                             "probably already running."),
                           strDataDir, _(PACKAGE_NAME)));
         }
         if (probeOnly) {
             lock.unlock();
         }
     } catch (const boost::interprocess::interprocess_exception &e) {
         return InitError(strprintf(_("Cannot obtain a lock on data directory "
                                      "%s. %s is probably already running.") +
                                        " %s.",
                                    strDataDir, _(PACKAGE_NAME), e.what()));
     }
     return true;
 }
 
 bool AppInitSanityChecks() {
     // Step 4: sanity checks
 
     // Initialize elliptic curve code
     std::string sha256_algo = SHA256AutoDetect();
     LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
     RandomInit();
     ECC_Start();
     globalVerifyHandle.reset(new ECCVerifyHandle());
 
     // Sanity check
     if (!InitSanityCheck()) {
         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
     return LockDataDirectory(true);
 }
 
-bool AppInitMain(Config &config, boost::thread_group &threadGroup,
-                 CScheduler &scheduler) {
+bool AppInitMain(Config &config,
+                 HTTPRPCRequestProcessor &httpRPCRequestProcessor,
+                 boost::thread_group &threadGroup, CScheduler &scheduler) {
     const CChainParams &chainparams = config.GetChainParams();
     // Step 4a: application initialization
 
     // 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;
     }
 
 #ifndef WIN32
     CreatePidFile(GetPidFile(), getpid());
 #endif
 
     BCLog::Logger &logger = GetLogger();
 
     bool default_shrinkdebugfile = logger.DefaultShrinkDebugFile();
     if (gArgs.GetBoolArg("-shrinkdebugfile", default_shrinkdebugfile)) {
         // Do this first since it both loads a bunch of debug.log into memory,
         // and because this needs to happen before any other debug.log printing.
         logger.ShrinkDebugFile();
     }
 
     if (logger.fPrintToDebugLog) {
         logger.OpenDebugLog();
     }
 
     if (!logger.fLogTimestamps) {
         LogPrintf("Startup time: %s\n",
                   DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()));
     }
     LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
     LogPrintf("Using data directory %s\n", GetDataDir().string());
     LogPrintf(
         "Using config file %s\n",
         GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
     LogPrintf("Using at most %i automatic connections (%i file descriptors "
               "available)\n",
               nMaxConnections, nFD);
 
     InitSignatureCache();
     InitScriptExecutionCache();
 
     LogPrintf("Using %u threads for script verification\n",
               nScriptCheckThreads);
     if (nScriptCheckThreads) {
         for (int i = 0; i < nScriptCheckThreads - 1; i++) {
             threadGroup.create_thread(&ThreadScriptCheck);
         }
     }
 
     // Start the lightweight task scheduler thread
     CScheduler::Function serviceLoop =
         boost::bind(&CScheduler::serviceQueue, &scheduler);
     threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>,
                                           "scheduler", serviceLoop));
 
     /**
      * 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 (gArgs.GetBoolArg("-server", false)) {
         uiInterface.InitMessage.connect(SetRPCWarmupStatus);
-        if (!AppInitServers(config, threadGroup)) {
+        if (!AppInitServers(config, httpRPCRequestProcessor, threadGroup)) {
             return InitError(
                 _("Unable to start HTTP server. See debug log for details."));
         }
     }
 
 // Step 5: verify wallet database integrity
 #ifdef ENABLE_WALLET
     if (!CWallet::Verify(chainparams)) {
         return false;
     }
 #endif
     // 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.
 
     assert(!g_connman);
     g_connman = std::unique_ptr<CConnman>(
         new CConnman(config, GetRand(std::numeric_limits<uint64_t>::max()),
                      GetRand(std::numeric_limits<uint64_t>::max())));
     CConnman &connman = *g_connman;
 
     peerLogic.reset(new PeerLogicValidation(&connman, scheduler));
     RegisterValidationInterface(peerLogic.get());
 
     if (gArgs.IsArgSet("-onlynet")) {
         std::set<enum Network> nets;
         for (const std::string &snet : gArgs.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)) SetLimited(net);
         }
     }
 
     // Check for host lookup allowed before parsing any network related
     // parameters
     fNameLookup = gArgs.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
 
     bool proxyRandomize =
         gArgs.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 = gArgs.GetArg("-proxy", "");
     SetLimited(NET_TOR);
     if (proxyArg != "" && proxyArg != "0") {
         CService proxyAddr;
         if (!Lookup(proxyArg.c_str(), 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_TOR, addrProxy);
         SetNameProxy(addrProxy);
         // by default, -proxy sets onion as reachable, unless -noonion later
         SetLimited(NET_TOR, false);
     }
 
     // -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 = gArgs.GetArg("-onion", "");
     if (onionArg != "") {
         if (onionArg == "0") {   // Handle -noonion/-onion=0
             SetLimited(NET_TOR); // set onions as unreachable
         } else {
             CService onionProxy;
             if (!Lookup(onionArg.c_str(), 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_TOR, addrOnion);
             SetLimited(NET_TOR, false);
         }
     }
 
     // see Step 2: parameter interactions for more information about these
     fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN);
     fDiscover = gArgs.GetBoolArg("-discover", true);
     fRelayTxes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
 
     for (const std::string &strAddr : gArgs.GetArgs("-externalip")) {
         CService addrLocal;
         if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) &&
             addrLocal.IsValid()) {
             AddLocal(addrLocal, LOCAL_MANUAL);
         } else {
             return InitError(ResolveErrMsg("externalip", strAddr));
         }
     }
 
 #if ENABLE_ZMQ
     pzmqNotificationInterface = CZMQNotificationInterface::Create();
 
     if (pzmqNotificationInterface) {
         RegisterValidationInterface(pzmqNotificationInterface);
     }
 #endif
     // unlimited unless -maxuploadtarget is set
     uint64_t nMaxOutboundLimit = 0;
     uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME;
 
     if (gArgs.IsArgSet("-maxuploadtarget")) {
         nMaxOutboundLimit =
             gArgs.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET) * 1024 *
             1024;
     }
 
     // Step 7: load block chain
 
     fReindex = gArgs.GetBoolArg("-reindex", false);
     bool fReindexChainState = gArgs.GetBoolArg("-reindex-chainstate", false);
 
     // cache size calculations
     int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20);
     // total cache cannot be less than nMinDbCache
     nTotalCache = std::max(nTotalCache, nMinDbCache << 20);
     // total cache cannot be greater than nMaxDbcache
     nTotalCache = std::min(nTotalCache, nMaxDbCache << 20);
     int64_t nBlockTreeDBCache = nTotalCache / 8;
     nBlockTreeDBCache = std::min(nBlockTreeDBCache,
                                  (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)
                                       ? nMaxBlockDBAndTxIndexCache
                                       : nMaxBlockDBCache)
                                      << 20);
     nTotalCache -= nBlockTreeDBCache;
     // use 25%-50% of the remainder for disk cache
     int64_t nCoinDBCache =
         std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23));
     // cap total coins db cache
     nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20);
     nTotalCache -= nCoinDBCache;
     // the rest goes to in-memory cache
     nCoinCacheUsage = nTotalCache;
     int64_t nMempoolSizeMax =
         gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
     LogPrintf("Cache configuration:\n");
     LogPrintf("* Using %.1fMiB for block index database\n",
               nBlockTreeDBCache * (1.0 / 1024 / 1024));
     LogPrintf("* Using %.1fMiB for chain state database\n",
               nCoinDBCache * (1.0 / 1024 / 1024));
     LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of "
               "unused mempool space)\n",
               nCoinCacheUsage * (1.0 / 1024 / 1024),
               nMempoolSizeMax * (1.0 / 1024 / 1024));
 
     int64_t nStart = 0;
     bool fLoaded = false;
     while (!fLoaded && !fRequestShutdown) {
         bool fReset = fReindex;
         std::string strLoadError;
 
         uiInterface.InitMessage(_("Loading block index..."));
 
         nStart = GetTimeMillis();
         do {
             try {
                 UnloadBlockIndex();
                 delete pcoinsTip;
                 delete pcoinsdbview;
                 delete pcoinscatcher;
                 delete pblocktree;
 
                 pblocktree =
                     new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
                 pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false,
                                                 fReindex || fReindexChainState);
                 pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
 
                 if (fReindex) {
                     pblocktree->WriteReindexing(true);
                     // If we're reindexing in prune mode, wipe away unusable
                     // block files and all undo data files
                     if (fPruneMode) {
                         CleanupBlockRevFiles();
                     }
                 } else if (!pcoinsdbview->Upgrade()) {
                     strLoadError = _("Error upgrading chainstate database");
                     break;
                 }
                 if (fRequestShutdown) {
                     break;
                 }
 
                 if (!LoadBlockIndex(config)) {
                     strLoadError = _("Error loading block database");
                     break;
                 }
 
                 // If the loaded chain has a wrong genesis, bail out immediately
                 // (we're likely using a testnet datadir, or the other way
                 // around).
                 if (!mapBlockIndex.empty() &&
                     mapBlockIndex.count(
                         chainparams.GetConsensus().hashGenesisBlock) == 0) {
                     return InitError(_("Incorrect or no genesis block found. "
                                        "Wrong datadir for network?"));
                 }
 
                 // Initialize the block index (no-op if non-empty database was
                 // already loaded)
                 if (!InitBlockIndex(config)) {
                     strLoadError = _("Error initializing block database");
                     break;
                 }
 
                 // Check for changed -txindex state
                 if (fTxIndex != gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
                     strLoadError = _("You need to rebuild the database using "
                                      "-reindex-chainstate to change -txindex");
                     break;
                 }
 
                 // Check for changed -prune state.  What we are concerned about
                 // is a user who has pruned blocks in the past, but is now
                 // trying to run unpruned.
                 if (fHavePruned && !fPruneMode) {
                     strLoadError =
                         _("You need to rebuild the database using -reindex to "
                           "go back to unpruned mode.  This will redownload the "
                           "entire blockchain");
                     break;
                 }
 
                 if (!ReplayBlocks(config, pcoinsdbview)) {
                     strLoadError =
                         _("Unable to replay blocks. You will need to rebuild "
                           "the database using -reindex-chainstate.");
                     break;
                 }
 
                 pcoinsTip = new CCoinsViewCache(pcoinscatcher);
                 LoadChainTip(chainparams);
 
                 if (!fReindex && chainActive.Tip() != nullptr) {
                     uiInterface.InitMessage(_("Rewinding blocks..."));
                     if (!RewindBlockIndex(config)) {
                         strLoadError = _("Unable to rewind the database to a "
                                          "pre-fork state. You will need to "
                                          "redownload the blockchain");
                         break;
                     }
                 }
 
                 uiInterface.InitMessage(_("Verifying blocks..."));
                 if (fHavePruned &&
                     gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) >
                         MIN_BLOCKS_TO_KEEP) {
                     LogPrintf("Prune: pruned datadir may not have more than %d "
                               "blocks; only checking available blocks",
                               MIN_BLOCKS_TO_KEEP);
                 }
 
                 {
                     LOCK(cs_main);
                     CBlockIndex *tip = chainActive.Tip();
                     RPCNotifyBlockChange(true, tip);
                     if (tip &&
                         tip->nTime >
                             GetAdjustedTime() + MAX_FUTURE_BLOCK_TIME) {
                         strLoadError =
                             _("The block database contains a block which "
                               "appears to be from the future. "
                               "This may be due to your computer's date and "
                               "time being set incorrectly. "
                               "Only rebuild the block database if you are sure "
                               "that your computer's date and time are correct");
                         break;
                     }
                 }
 
                 if (!CVerifyDB().VerifyDB(
                         config, pcoinsdbview,
                         gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
                         gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
                     strLoadError = _("Corrupted block database detected");
                     break;
                 }
             } catch (const std::exception &e) {
                 LogPrintf("%s\n", e.what());
                 strLoadError = _("Error opening block database");
                 break;
             }
 
             fLoaded = true;
         } while (false);
 
         if (!fLoaded && !fRequestShutdown) {
             // first suggest a reindex
             if (!fReset) {
                 bool fRet = uiInterface.ThreadSafeQuestion(
                     strLoadError + ".\n\n" +
                         _("Do you want to rebuild the block database now?"),
                     strLoadError + ".\nPlease restart with -reindex or "
                                    "-reindex-chainstate to recover.",
                     "",
                     CClientUIInterface::MSG_ERROR |
                         CClientUIInterface::BTN_ABORT);
                 if (fRet) {
                     fReindex = true;
                     fRequestShutdown = false;
                 } else {
                     LogPrintf("Aborted block database rebuild. Exiting.\n");
                     return false;
                 }
             } else {
                 return InitError(strLoadError);
             }
         }
     }
 
     // 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 (fRequestShutdown) {
         LogPrintf("Shutdown requested. Exiting.\n");
         return false;
     }
     LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart);
 
     fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
     CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK,
                          CLIENT_VERSION);
     // Allowed to fail as this file IS missing on first startup.
     if (!est_filein.IsNull()) {
         mempool.ReadFeeEstimates(est_filein);
     }
     fFeeEstimatesInitialized = true;
 
     // Encoded addresses using cashaddr instead of base58
     // Activates by default on Jan, 14
     config.SetCashAddrEncoding(
         gArgs.GetBoolArg("-usecashaddr", GetAdjustedTime() > 1515900000));
 
 // Step 8: load wallet
 #ifdef ENABLE_WALLET
     if (!CWallet::InitLoadWallet(chainparams)) {
         return false;
     }
 #else
     LogPrintf("No wallet support compiled in!\n");
 #endif
 
     // Step 9: data directory maintenance
 
     // if pruning, unset the service bit and perform the initial blockstore
     // prune after any wallet rescanning has taken place.
     if (fPruneMode) {
         LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
         nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
         if (!fReindex) {
             uiInterface.InitMessage(_("Pruning blockstore..."));
             PruneAndFlush();
         }
     }
 
     // Step 10: import blocks
     if (!CheckDiskSpace()) {
         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.
     if (chainActive.Tip() == nullptr) {
         uiInterface.NotifyBlockTip.connect(BlockNotifyGenesisWait);
     } else {
         fHaveGenesis = true;
     }
 
     if (gArgs.IsArgSet("-blocknotify")) {
         uiInterface.NotifyBlockTip.connect(BlockNotifyCallback);
     }
 
     std::vector<fs::path> vImportFiles;
     for (const std::string &strFile : gArgs.GetArgs("-loadblock")) {
         vImportFiles.push_back(strFile);
     }
 
     threadGroup.create_thread(
         boost::bind(&ThreadImport, std::ref(config), vImportFiles));
 
     // Wait for genesis block to be processed
     {
         boost::unique_lock<boost::mutex> lock(cs_GenesisWait);
         while (!fHaveGenesis) {
             condvar_GenesisWait.wait(lock);
         }
         uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait);
     }
 
     // Step 11: start node
 
     //// debug print
     LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
     LogPrintf("nBestHeight = %d\n", chainActive.Height());
     if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
         StartTorControl();
     }
 
     Discover();
 
     // Map ports with UPnP
     if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) {
         StartMapPort();
     }
 
     CConnman::Options connOptions;
     connOptions.nLocalServices = nLocalServices;
     connOptions.nMaxConnections = nMaxConnections;
     connOptions.nMaxOutbound =
         std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
     connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
     connOptions.nMaxFeeler = 1;
     connOptions.nBestHeight = chainActive.Height();
     connOptions.uiInterface = &uiInterface;
     connOptions.m_msgproc = peerLogic.get();
     connOptions.nSendBufferMaxSize =
         1000 * gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
     connOptions.nReceiveFloodSize =
         1000 * gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
     connOptions.m_added_nodes = gArgs.GetArgs("-addnode");
 
     connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
     connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
 
     for (const std::string &strBind : gArgs.GetArgs("-bind")) {
         CService addrBind;
         if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) {
             return InitError(ResolveErrMsg("bind", strBind));
         }
         connOptions.vBinds.push_back(addrBind);
     }
 
     for (const std::string &strBind : gArgs.GetArgs("-whitebind")) {
         CService addrBind;
         if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
             return InitError(ResolveErrMsg("whitebind", strBind));
         }
         if (addrBind.GetPort() == 0) {
             return InitError(strprintf(
                 _("Need to specify a port with -whitebind: '%s'"), strBind));
         }
         connOptions.vWhiteBinds.push_back(addrBind);
     }
 
     for (const auto &net : gArgs.GetArgs("-whitelist")) {
         CSubNet subnet;
         LookupSubNet(net.c_str(), subnet);
         if (!subnet.IsValid()) {
             return InitError(strprintf(
                 _("Invalid netmask specified in -whitelist: '%s'"), net));
         }
         connOptions.vWhitelistedRange.push_back(subnet);
     }
 
     connOptions.vSeedNodes = gArgs.GetArgs("-seednode");
 
     // Initiate outbound connections unless connect=0
     connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect");
     if (!connOptions.m_use_addrman_outgoing) {
         const auto connect = gArgs.GetArgs("-connect");
         if (connect.size() != 1 || connect[0] != "0") {
             connOptions.m_specified_outgoing = connect;
         }
     }
     if (!connman.Start(scheduler, connOptions)) {
         return false;
     }
 
     // Step 12: finished
 
     SetRPCWarmupFinished();
     uiInterface.InitMessage(_("Done loading"));
 
 #ifdef ENABLE_WALLET
     for (CWalletRef pwallet : vpwallets) {
         pwallet->postInitProcess(scheduler);
     }
 #endif
 
     return !fRequestShutdown;
 }
diff --git a/src/init.h b/src/init.h
index d70bddadd..4363abebf 100644
--- a/src/init.h
+++ b/src/init.h
@@ -1,65 +1,67 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2016 The Bitcoin Core developers
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 #ifndef BITCOIN_INIT_H
 #define BITCOIN_INIT_H
 
 #include <string>
 
 class Config;
 class CScheduler;
 class CWallet;
+class HTTPRPCRequestProcessor;
 
 namespace boost {
 class thread_group;
 } // namespace boost
 
 void StartShutdown();
 bool ShutdownRequested();
 /** Interrupt threads */
 void Interrupt(boost::thread_group &threadGroup);
 void Shutdown();
 //! Initialize the logging infrastructure
 void InitLogging();
 //! Parameter interaction: change current parameters depending on various rules
 void InitParameterInteraction();
 
 /** Initialize bitcoin core: Basic context setup.
  *  @note This can be done before daemonization.
  *  @pre Parameters should be parsed and config file should be read.
  */
 bool AppInitBasicSetup();
 /**
  * Initialization: parameter interaction.
  * @note This can be done before daemonization.
  * @pre Parameters should be parsed and config file should be read,
  * AppInitBasicSetup should have been called.
  */
 bool AppInitParameterInteraction(Config &config);
 /**
  * Initialization sanity checks: ecc init, sanity checks, dir lock.
  * @note This can be done before daemonization.
  * @pre Parameters should be parsed and config file should be read,
  * AppInitParameterInteraction should have been called.
  */
 bool AppInitSanityChecks();
 /**
  * Bitcoin core main initialization.
  * @note This should only be done after daemonization.
  * @pre Parameters should be parsed and config file should be read,
  * AppInitSanityChecks should have been called.
  */
-bool AppInitMain(Config &config, boost::thread_group &threadGroup,
-                 CScheduler &scheduler);
+bool AppInitMain(Config &config,
+                 HTTPRPCRequestProcessor &httpRPCRequestProcessor,
+                 boost::thread_group &threadGroup, CScheduler &scheduler);
 
 /** The help message mode determines what help message to show */
 enum HelpMessageMode { HMM_BITCOIND, HMM_BITCOIN_QT };
 
 /** Help for options shared between UI and daemon (for -help) */
 std::string HelpMessage(HelpMessageMode mode);
 /** Returns licensing information (for -version) */
 std::string LicenseInfo();
 
 #endif // BITCOIN_INIT_H
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 52b542bac..a051d2331 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -1,806 +1,816 @@
 // Copyright (c) 2011-2016 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 "bitcoingui.h"
 
 #include "chainparams.h"
 #include "clientmodel.h"
 #include "config.h"
 #include "guiconstants.h"
 #include "guiutil.h"
+#include "httprpc.h"
 #include "intro.h"
 #include "networkstyle.h"
 #include "optionsmodel.h"
 #include "platformstyle.h"
 #include "splashscreen.h"
 #include "utilitydialog.h"
 #include "winshutdownmonitor.h"
 
 #ifdef ENABLE_WALLET
 #include "paymentserver.h"
 #include "walletmodel.h"
 #endif
 
 #include "init.h"
 #include "rpc/server.h"
 #include "scheduler.h"
 #include "ui_interface.h"
 #include "util.h"
 #include "warnings.h"
 
 #ifdef ENABLE_WALLET
 #include "wallet/wallet.h"
 #endif
 
 #include <cstdint>
 
 #include <boost/filesystem/operations.hpp>
 #include <boost/thread.hpp>
 
 #include <QApplication>
 #include <QDebug>
 #include <QLibraryInfo>
 #include <QLocale>
 #include <QMessageBox>
 #include <QSettings>
 #include <QSslConfiguration>
 #include <QStringList>
 #include <QThread>
 #include <QTimer>
 #include <QTranslator>
 
 #if defined(QT_STATICPLUGIN)
 #include <QtPlugin>
 #if QT_VERSION < 0x050000
 Q_IMPORT_PLUGIN(qcncodecs)
 Q_IMPORT_PLUGIN(qjpcodecs)
 Q_IMPORT_PLUGIN(qtwcodecs)
 Q_IMPORT_PLUGIN(qkrcodecs)
 Q_IMPORT_PLUGIN(qtaccessiblewidgets)
 #else
 #if QT_VERSION < 0x050400
 Q_IMPORT_PLUGIN(AccessibleFactory)
 #endif
 #if defined(QT_QPA_PLATFORM_XCB)
 Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
 #elif defined(QT_QPA_PLATFORM_WINDOWS)
 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
 #elif defined(QT_QPA_PLATFORM_COCOA)
 Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
 #endif
 #endif
 #endif
 
 #if QT_VERSION < 0x050000
 #include <QTextCodec>
 #endif
 
 // Declare meta types used for QMetaObject::invokeMethod
 Q_DECLARE_METATYPE(bool *)
 Q_DECLARE_METATYPE(Amount)
 
 // Config is non-copyable so we can only register pointers to it
 Q_DECLARE_METATYPE(Config *)
 
 static void InitMessage(const std::string &message) {
     LogPrintf("init message: %s\n", message);
 }
 
 /**
  * Translate string to current locale using Qt.
  */
 static std::string Translate(const char *psz) {
     return QCoreApplication::translate("bitcoin-abc", psz).toStdString();
 }
 
 static QString GetLangTerritory() {
     QSettings settings;
     // Get desired locale (e.g. "de_DE")
     // 1) System default language
     QString lang_territory = QLocale::system().name();
     // 2) Language from QSettings
     QString lang_territory_qsettings =
         settings.value("language", "").toString();
     if (!lang_territory_qsettings.isEmpty())
         lang_territory = lang_territory_qsettings;
     // 3) -lang command line argument
     lang_territory = QString::fromStdString(
         gArgs.GetArg("-lang", lang_territory.toStdString()));
     return lang_territory;
 }
 
 /** Set up translations */
 static void initTranslations(QTranslator &qtTranslatorBase,
                              QTranslator &qtTranslator,
                              QTranslator &translatorBase,
                              QTranslator &translator) {
     // Remove old translators
     QApplication::removeTranslator(&qtTranslatorBase);
     QApplication::removeTranslator(&qtTranslator);
     QApplication::removeTranslator(&translatorBase);
     QApplication::removeTranslator(&translator);
 
     // Get desired locale (e.g. "de_DE")
     // 1) System default language
     QString lang_territory = GetLangTerritory();
 
     // Convert to "de" only by truncating "_DE"
     QString lang = lang_territory;
     lang.truncate(lang_territory.lastIndexOf('_'));
 
     // Load language files for configured locale:
     // - First load the translator for the base language, without territory
     // - Then load the more specific locale translator
 
     // Load e.g. qt_de.qm
     if (qtTranslatorBase.load(
             "qt_" + lang,
             QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
         QApplication::installTranslator(&qtTranslatorBase);
 
     // Load e.g. qt_de_DE.qm
     if (qtTranslator.load(
             "qt_" + lang_territory,
             QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
         QApplication::installTranslator(&qtTranslator);
 
     // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in
     // bitcoin.qrc)
     if (translatorBase.load(lang, ":/translations/"))
         QApplication::installTranslator(&translatorBase);
 
     // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in
     // bitcoin.qrc)
     if (translator.load(lang_territory, ":/translations/"))
         QApplication::installTranslator(&translator);
 }
 
 /* qDebug() message handler --> debug.log */
 #if QT_VERSION < 0x050000
 void DebugMessageHandler(QtMsgType type, const char *msg) {
     if (type == QtDebugMsg) {
         LogPrint(BCLog::QT, "GUI: %s\n", msg);
     } else {
         LogPrintf("GUI: %s\n", msg);
     }
 }
 #else
 void DebugMessageHandler(QtMsgType type, const QMessageLogContext &context,
                          const QString &msg) {
     Q_UNUSED(context);
     if (type == QtDebugMsg) {
         LogPrint(BCLog::QT, "GUI: %s\n", msg.toStdString());
     } else {
         LogPrintf("GUI: %s\n", msg.toStdString());
     }
 }
 #endif
 
 /**
  * Class encapsulating Bitcoin ABC startup and shutdown.
  * Allows running startup and shutdown in a different thread from the UI thread.
  */
 class BitcoinABC : public QObject {
     Q_OBJECT
 public:
     explicit BitcoinABC();
 
 public Q_SLOTS:
-    void initialize(Config *config);
+    void initialize(Config *config,
+                    HTTPRPCRequestProcessor *httpRPCRequestProcessor);
     void shutdown();
 
 Q_SIGNALS:
     void initializeResult(int retval);
     void shutdownResult(int retval);
     void runawayException(const QString &message);
 
 private:
     boost::thread_group threadGroup;
     CScheduler scheduler;
 
     /// Pass fatal exception message to UI thread
     void handleRunawayException(const std::exception *e);
 };
 
 /** Main Bitcoin application object */
 class BitcoinApplication : public QApplication {
     Q_OBJECT
 public:
     explicit BitcoinApplication(int &argc, char **argv);
     ~BitcoinApplication();
 
 #ifdef ENABLE_WALLET
     /// Create payment server
     void createPaymentServer();
 #endif
     /// parameter interaction/setup based on rules
     void parameterSetup();
     /// Create options model
     void createOptionsModel(bool resetSettings);
     /// Create main window
     void createWindow(const Config *, const NetworkStyle *networkStyle);
     /// Create splash screen
     void createSplashScreen(const NetworkStyle *networkStyle);
 
     /// Request core initialization
-    void requestInitialize(Config &config);
+    void requestInitialize(Config &config,
+                           HTTPRPCRequestProcessor &httpRPCRequestProcessor);
     /// Request core shutdown
     void requestShutdown(Config &config);
 
     /// Get process return value
     int getReturnValue() { return returnValue; }
 
     /// Get window identifier of QMainWindow (BitcoinGUI)
     WId getMainWinId() const;
 
 public Q_SLOTS:
     void initializeResult(int retval);
     void shutdownResult(int retval);
     /// Handle runaway exceptions. Shows a message box with the problem and
     /// quits the program.
     void handleRunawayException(const QString &message);
 
 Q_SIGNALS:
-    void requestedInitialize(Config *config);
+    void requestedInitialize(Config *config,
+                             HTTPRPCRequestProcessor *httpRPCRequestProcessor);
     void requestedShutdown();
     void stopThread();
     void splashFinished(QWidget *window);
 
 private:
     QThread *coreThread;
     OptionsModel *optionsModel;
     ClientModel *clientModel;
     BitcoinGUI *window;
     QTimer *pollShutdownTimer;
 #ifdef ENABLE_WALLET
     PaymentServer *paymentServer;
     WalletModel *walletModel;
 #endif
     int returnValue;
     const PlatformStyle *platformStyle;
     std::unique_ptr<QWidget> shutdownWindow;
 
     void startThread();
 };
 
 #include "bitcoin.moc"
 
 BitcoinABC::BitcoinABC() : QObject() {}
 
 void BitcoinABC::handleRunawayException(const std::exception *e) {
     PrintExceptionContinue(e, "Runaway exception");
     Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui")));
 }
 
-void BitcoinABC::initialize(Config *cfg) {
+void BitcoinABC::initialize(Config *cfg,
+                            HTTPRPCRequestProcessor *httpRPCRequestProcessor) {
     Config &config(*cfg);
     try {
         qDebug() << __func__ << ": Running AppInit2 in thread";
         if (!AppInitBasicSetup()) {
             Q_EMIT initializeResult(false);
             return;
         }
 
         if (!AppInitParameterInteraction(config)) {
             Q_EMIT initializeResult(false);
             return;
         }
 
         if (!AppInitSanityChecks()) {
             Q_EMIT initializeResult(false);
             return;
         }
 
-        int rv = AppInitMain(config, threadGroup, scheduler);
+        int rv = AppInitMain(config, *httpRPCRequestProcessor, threadGroup,
+                             scheduler);
         Q_EMIT initializeResult(rv);
     } catch (const std::exception &e) {
         handleRunawayException(&e);
     } catch (...) {
         handleRunawayException(nullptr);
     }
 }
 
 void BitcoinABC::shutdown() {
     try {
         qDebug() << __func__ << ": Running Shutdown in thread";
         Interrupt(threadGroup);
         threadGroup.join_all();
         Shutdown();
         qDebug() << __func__ << ": Shutdown finished";
         Q_EMIT shutdownResult(1);
     } catch (const std::exception &e) {
         handleRunawayException(&e);
     } catch (...) {
         handleRunawayException(nullptr);
     }
 }
 
 BitcoinApplication::BitcoinApplication(int &argc, char **argv)
     : QApplication(argc, argv), coreThread(0), optionsModel(0), clientModel(0),
       window(0), pollShutdownTimer(0),
 #ifdef ENABLE_WALLET
       paymentServer(0), walletModel(0),
 #endif
       returnValue(0) {
     setQuitOnLastWindowClosed(false);
 
     // UI per-platform customization.
     // This must be done inside the BitcoinApplication constructor, or after it,
     // because PlatformStyle::instantiate requires a QApplication.
     std::string platformName;
     platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM);
     platformStyle =
         PlatformStyle::instantiate(QString::fromStdString(platformName));
     // Fall back to "other" if specified name not found.
     if (!platformStyle) platformStyle = PlatformStyle::instantiate("other");
     assert(platformStyle);
 }
 
 BitcoinApplication::~BitcoinApplication() {
     if (coreThread) {
         qDebug() << __func__ << ": Stopping thread";
         Q_EMIT stopThread();
         coreThread->wait();
         qDebug() << __func__ << ": Stopped thread";
     }
 
     delete window;
     window = 0;
 #ifdef ENABLE_WALLET
     delete paymentServer;
     paymentServer = 0;
 #endif
     delete optionsModel;
     optionsModel = 0;
     delete platformStyle;
     platformStyle = 0;
 }
 
 #ifdef ENABLE_WALLET
 void BitcoinApplication::createPaymentServer() {
     paymentServer = new PaymentServer(this);
 }
 #endif
 
 void BitcoinApplication::createOptionsModel(bool resetSettings) {
     optionsModel = new OptionsModel(nullptr, resetSettings);
 }
 
 void BitcoinApplication::createWindow(const Config *config,
                                       const NetworkStyle *networkStyle) {
     window = new BitcoinGUI(config, platformStyle, networkStyle, 0);
 
     pollShutdownTimer = new QTimer(window);
     connect(pollShutdownTimer, SIGNAL(timeout()), window,
             SLOT(detectShutdown()));
     pollShutdownTimer->start(200);
 }
 
 void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) {
     SplashScreen *splash = new SplashScreen(0, networkStyle);
     // We don't hold a direct pointer to the splash screen after creation, but
     // the splash screen will take care of deleting itself when slotFinish
     // happens.
     splash->show();
     connect(this, SIGNAL(splashFinished(QWidget *)), splash,
             SLOT(slotFinish(QWidget *)));
     connect(this, SIGNAL(requestedShutdown()), splash, SLOT(close()));
 }
 
 void BitcoinApplication::startThread() {
     if (coreThread) return;
     coreThread = new QThread(this);
     BitcoinABC *executor = new BitcoinABC();
     executor->moveToThread(coreThread);
 
     /*  communication to and from thread */
     connect(executor, SIGNAL(initializeResult(int)), this,
             SLOT(initializeResult(int)));
     connect(executor, SIGNAL(shutdownResult(int)), this,
             SLOT(shutdownResult(int)));
     connect(executor, SIGNAL(runawayException(QString)), this,
             SLOT(handleRunawayException(QString)));
 
     // Note on how Qt works: it tries to directly invoke methods if the signal
     // is emitted on the same thread that the target object 'lives' on.
     // But if the target object 'lives' on another thread (executor here does)
     // the SLOT will be invoked asynchronously at a later time in the thread
     // of the target object.  So.. we pass a pointer around.  If you pass
     // a reference around (even if it's non-const) you'll get Qt generating
     // code to copy-construct the parameter in question (Q_DECLARE_METATYPE
     // and qRegisterMetaType generate this code).  For the Config class,
     // which is noncopyable, we can't do this.  So.. we have to pass
     // pointers to Config around.  Make sure Config &/Config * isn't a
     // temporary (eg it lives somewhere aside from the stack) or this will
     // crash because initialize() gets executed in another thread at some
     // unspecified time (after) requestedInitialize() is emitted!
-    connect(this, SIGNAL(requestedInitialize(Config *)), executor,
-            SLOT(initialize(Config *)));
+    connect(this,
+            SIGNAL(requestedInitialize(Config *, HTTPRPCRequestProcessor *)),
+            executor, SLOT(initialize(Config *, HTTPRPCRequestProcessor *)));
 
     connect(this, SIGNAL(requestedShutdown()), executor, SLOT(shutdown()));
     /*  make sure executor object is deleted in its own thread */
     connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
     connect(this, SIGNAL(stopThread()), coreThread, SLOT(quit()));
 
     coreThread->start();
 }
 
 void BitcoinApplication::parameterSetup() {
     InitLogging();
     InitParameterInteraction();
 }
 
-void BitcoinApplication::requestInitialize(Config &config) {
+void BitcoinApplication::requestInitialize(
+    Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor) {
     qDebug() << __func__ << ": Requesting initialize";
     startThread();
     // IMPORTANT: config must NOT be a reference to a temporary because below
     // signal may be connected to a slot that will be executed as a queued
     // connection in another thread!
-    Q_EMIT requestedInitialize(&config);
+    Q_EMIT requestedInitialize(&config, &httpRPCRequestProcessor);
 }
 
 void BitcoinApplication::requestShutdown(Config &config) {
     // Show a simple window indicating shutdown status. Do this first as some of
     // the steps may take some time below, for example the RPC console may still
     // be executing a command.
     shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
 
     qDebug() << __func__ << ": Requesting shutdown";
     startThread();
     window->hide();
     window->setClientModel(0);
     pollShutdownTimer->stop();
 
 #ifdef ENABLE_WALLET
     window->removeAllWallets();
     delete walletModel;
     walletModel = 0;
 #endif
     delete clientModel;
     clientModel = 0;
 
     StartShutdown();
 
     // Request shutdown from core thread
     Q_EMIT requestedShutdown();
 }
 
 void BitcoinApplication::initializeResult(int retval) {
     qDebug() << __func__ << ": Initialization result: " << retval;
     // Set exit result: 0 if successful, 1 if failure
     returnValue = retval ? 0 : 1;
     if (retval) {
         // Log this only after AppInit2 finishes, as then logging setup is
         // guaranteed complete.
         qWarning() << "Platform customization:" << platformStyle->getName();
 #ifdef ENABLE_WALLET
         PaymentServer::LoadRootCAs();
         paymentServer->setOptionsModel(optionsModel);
 #endif
 
         clientModel = new ClientModel(optionsModel);
         window->setClientModel(clientModel);
 
 #ifdef ENABLE_WALLET
         // TODO: Expose secondary wallets
         if (!vpwallets.empty()) {
             walletModel =
                 new WalletModel(platformStyle, vpwallets[0], optionsModel);
 
             window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel);
             window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET);
 
             connect(walletModel, SIGNAL(coinsSent(CWallet *, SendCoinsRecipient,
                                                   QByteArray)),
                     paymentServer,
                     SLOT(fetchPaymentACK(CWallet *, const SendCoinsRecipient &,
                                          QByteArray)));
         }
 #endif
 
         // If -min option passed, start window minimized.
         if (gArgs.GetBoolArg("-min", false)) {
             window->showMinimized();
         } else {
             window->show();
         }
         Q_EMIT splashFinished(window);
 
 #ifdef ENABLE_WALLET
         // Now that initialization/startup is done, process any command-line
         // bitcoincash: URIs or payment requests:
         connect(paymentServer,
                 SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), window,
                 SLOT(handlePaymentRequest(SendCoinsRecipient)));
         connect(window, SIGNAL(receivedURI(QString)), paymentServer,
                 SLOT(handleURIOrFile(QString)));
         connect(paymentServer, SIGNAL(message(QString, QString, unsigned int)),
                 window, SLOT(message(QString, QString, unsigned int)));
         QTimer::singleShot(100, paymentServer, SLOT(uiReady()));
 #endif
     } else {
         // Exit main loop.
         quit();
     }
 }
 
 void BitcoinApplication::shutdownResult(int retval) {
     qDebug() << __func__ << ": Shutdown result: " << retval;
     // Exit main loop after shutdown finished.
     quit();
 }
 
 void BitcoinApplication::handleRunawayException(const QString &message) {
     QMessageBox::critical(
         0, "Runaway exception",
         BitcoinGUI::tr("A fatal error occurred. Bitcoin can no longer continue "
                        "safely and will quit.") +
             QString("\n\n") + message);
     ::exit(EXIT_FAILURE);
 }
 
 WId BitcoinApplication::getMainWinId() const {
     if (!window) return 0;
 
     return window->winId();
 }
 
 #ifndef BITCOIN_QT_TEST
 
 static void MigrateSettings() {
     assert(!QApplication::applicationName().isEmpty());
 
     static const QString legacyAppName("Bitcoin-Qt"),
 #ifdef Q_OS_DARWIN
         // Macs and/or iOS et al use a domain-style name for Settings
         // files. All other platforms use a simple orgname. This
         // difference is documented in the QSettings class documentation.
         legacyOrg("bitcoin.org");
 #else
         legacyOrg("Bitcoin");
 #endif
     QSettings
         // below picks up settings file location based on orgname,appname
         legacy(legacyOrg, legacyAppName),
         // default c'tor below picks up settings file location based on
         // QApplication::applicationName(), et al -- which was already set
         // in main()
         abc;
 #ifdef Q_OS_DARWIN
     // Disable bogus OSX keys from MacOS system-wide prefs that may cloud our
     // judgement ;) (this behavior is also documented in QSettings docs)
     legacy.setFallbacksEnabled(false);
     abc.setFallbacksEnabled(false);
 #endif
     const QStringList legacyKeys(legacy.allKeys());
 
     // We only migrate settings if we have Core settings but no Bitcoin-ABC
     // settings
     if (!legacyKeys.isEmpty() && abc.allKeys().isEmpty()) {
         for (const QString &key : legacyKeys) {
             // now, copy settings over
             abc.setValue(key, legacy.value(key));
         }
     }
 }
 
 int main(int argc, char *argv[]) {
     SetupEnvironment();
 
     /// 1. Parse command-line options. These take precedence over anything else.
     // Command-line options take precedence:
     gArgs.ParseParameters(argc, argv);
 
 // Do not refer to data directory yet, this can be overridden by
 // Intro::pickDataDirectory
 
 /// 2. Basic Qt initialization (not dependent on parameters or configuration)
 #if QT_VERSION < 0x050000
     // Internal string conversion is all UTF-8
     QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
     QTextCodec::setCodecForCStrings(QTextCodec::codecForTr());
 #endif
 
     Q_INIT_RESOURCE(bitcoin);
     Q_INIT_RESOURCE(bitcoin_locale);
 
     BitcoinApplication app(argc, argv);
 #if QT_VERSION > 0x050100
     // Generate high-dpi pixmaps
     QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
 #endif
 #if QT_VERSION >= 0x050600
     QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
 #endif
 #ifdef Q_OS_MAC
     QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
 #endif
 #if QT_VERSION >= 0x050500
     // Because of the POODLE attack it is recommended to disable SSLv3
     // (https://disablessl3.com/), so set SSL protocols to TLS1.0+.
     QSslConfiguration sslconf = QSslConfiguration::defaultConfiguration();
     sslconf.setProtocol(QSsl::TlsV1_0OrLater);
     QSslConfiguration::setDefaultConfiguration(sslconf);
 #endif
 
     // Register meta types used for QMetaObject::invokeMethod
     qRegisterMetaType<bool *>();
     //   Need to pass name here as Amount is a typedef (see
     //   http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
     //   IMPORTANT if it is no longer a typedef use the normal variant above
     qRegisterMetaType<Amount>("Amount");
     qRegisterMetaType<std::function<void(void)>>("std::function<void(void)>");
 
     // Need to register any types Qt doesn't know about if you intend
     // to use them with the signal/slot mechanism Qt provides. Even pointers.
     // Note that class Config is noncopyable and so we can't register a
     // non-pointer version of it with Qt, because Qt expects to be able to
     // copy-construct non-pointers to objects for invoking slots
     // behind-the-scenes in the 'Queued' connection case.
     qRegisterMetaType<Config *>();
 
     /// 3. Application identification
     // must be set before OptionsModel is initialized or translations are
     // loaded, as it is used to locate QSettings.
     // Note: If you move these calls somewhere else, be sure to bring
     // MigrateSettings() below along for the ride.
     QApplication::setOrganizationName(QAPP_ORG_NAME);
     QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN);
     QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT);
     // Migrate settings from core's/our old GUI settings to Bitcoin ABC
     // only if core's exist but Bitcoin ABC's doesn't.
     // NOTE -- this function needs to be called *after* the above 3 lines
     // that set the app orgname and app name! If you move the above 3 lines
     // to elsewhere, take this call with you!
     MigrateSettings();
     GUIUtil::SubstituteFonts(GetLangTerritory());
 
     /// 4. Initialization of translations, so that intro dialog is in user's
     /// language. Now that QSettings are accessible, initialize translations.
     QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
     initTranslations(qtTranslatorBase, qtTranslator, translatorBase,
                      translator);
     translationInterface.Translate.connect(Translate);
 
     // Show help message immediately after parsing command-line options (for
     // "-lang") and setting locale, but before showing splash screen.
     if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") ||
         gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) {
         HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version"));
         help.showOrPrint();
         return EXIT_SUCCESS;
     }
 
     /// 5. Now that settings and translations are available, ask user for data
     /// directory. User language is set up: pick a data directory.
     if (!Intro::pickDataDirectory()) return EXIT_SUCCESS;
 
     /// 6. Determine availability of data directory and parse bitcoin.conf
     /// - Do not call GetDataDir(true) before this step finishes.
     if (!fs::is_directory(GetDataDir(false))) {
         QMessageBox::critical(
             0, QObject::tr(PACKAGE_NAME),
             QObject::tr(
                 "Error: Specified data directory \"%1\" does not exist.")
                 .arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
         return EXIT_FAILURE;
     }
     try {
         gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
     } catch (const std::exception &e) {
         QMessageBox::critical(
             0, QObject::tr(PACKAGE_NAME),
             QObject::tr("Error: Cannot parse configuration file: %1. Only use "
                         "key=value syntax.")
                 .arg(e.what()));
         return EXIT_FAILURE;
     }
 
     /// 7. Determine network (and switch to network specific options)
     // - Do not call Params() before this step.
     // - Do this after parsing the configuration file, as the network can be
     // switched there.
     // - QSettings() will use the new application name after this, resulting in
     // network-specific settings.
     // - Needs to be done before createOptionsModel.
 
     // Check for -testnet or -regtest parameter (Params() calls are only valid
     // after this clause)
     try {
         SelectParams(ChainNameFromCommandLine());
     } catch (std::exception &e) {
         QMessageBox::critical(0, QObject::tr(PACKAGE_NAME),
                               QObject::tr("Error: %1").arg(e.what()));
         return EXIT_FAILURE;
     }
 #ifdef ENABLE_WALLET
     // Parse URIs on command line -- this can affect Params()
     PaymentServer::ipcParseCommandLine(argc, argv);
 #endif
 
     QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(
         QString::fromStdString(Params().NetworkIDString())));
     assert(!networkStyle.isNull());
     // Allow for separate UI settings for testnets
     QApplication::setApplicationName(networkStyle->getAppName());
     // Re-initialize translations after changing application name (language in
     // network-specific settings can be different)
     initTranslations(qtTranslatorBase, qtTranslator, translatorBase,
                      translator);
 
 #ifdef ENABLE_WALLET
     /// 8. URI IPC sending
     // - Do this early as we don't want to bother initializing if we are just
     // calling IPC
     // - Do this *after* setting up the data directory, as the data directory
     // hash is used in the name
     // of the server.
     // - Do this after creating app and setting up translations, so errors are
     // translated properly.
     if (PaymentServer::ipcSendCommandLine()) exit(EXIT_SUCCESS);
 
     // Start up the payment server early, too, so impatient users that click on
     // bitcoincash: links repeatedly have their payment requests routed to this
     // process:
     app.createPaymentServer();
 #endif
 
     /// 9. Main GUI initialization
     // Install global event filter that makes sure that long tooltips can be
     // word-wrapped.
     app.installEventFilter(
         new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
 #if QT_VERSION < 0x050000
     // Install qDebug() message handler to route to debug.log
     qInstallMsgHandler(DebugMessageHandler);
 #else
 #if defined(Q_OS_WIN)
     // Install global event filter for processing Windows session related
     // Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
     qApp->installNativeEventFilter(new WinShutdownMonitor());
 #endif
     // Install qDebug() message handler to route to debug.log
     qInstallMessageHandler(DebugMessageHandler);
 #endif
     // Allow parameter interaction before we create the options model
     app.parameterSetup();
     // Load GUI settings from QSettings
     app.createOptionsModel(gArgs.IsArgSet("-resetguisettings"));
 
     // Subscribe to global signals from core
     uiInterface.InitMessage.connect(InitMessage);
 
     // Get global config
     Config &config = const_cast<Config &>(GetConfig());
 
     if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) &&
         !gArgs.GetBoolArg("-min", false))
         app.createSplashScreen(networkStyle.data());
 
+    HTTPRPCRequestProcessor httpRPCRequestProcessor(config);
+
     try {
         app.createWindow(&config, networkStyle.data());
-        app.requestInitialize(config);
+        app.requestInitialize(config, httpRPCRequestProcessor);
 #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
         WinShutdownMonitor::registerShutdownBlockReason(
             QObject::tr("%1 didn't yet exit safely...")
                 .arg(QObject::tr(PACKAGE_NAME)),
             (HWND)app.getMainWinId());
 #endif
         app.exec();
         app.requestShutdown(config);
         app.exec();
     } catch (const std::exception &e) {
         PrintExceptionContinue(&e, "Runaway exception");
         app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
     } catch (...) {
         PrintExceptionContinue(nullptr, "Runaway exception");
         app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
     }
     return app.getReturnValue();
 }
 #endif // BITCOIN_QT_TEST