|
| 1 | +/* |
| 2 | + Copyright 2024 The Silkworm Authors |
| 3 | +
|
| 4 | + Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + you may not use this file except in compliance with the License. |
| 6 | + You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | + Unless required by applicable law or agreed to in writing, software |
| 11 | + distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + See the License for the specific language governing permissions and |
| 14 | + limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +#include <silkworm/infra/common/log.hpp> |
| 18 | +#include <silkworm/rpc/settings.hpp> |
| 19 | + |
| 20 | +#include "common.hpp" |
| 21 | +#include "instance.hpp" |
| 22 | +#include "silkworm.h" |
| 23 | + |
| 24 | +using namespace silkworm; |
| 25 | +using namespace silkworm::rpc; |
| 26 | +using silkworm::concurrency::ContextPoolSettings; |
| 27 | + |
| 28 | +//! Build interface log settings for ETH JSON-RPC from their C representation |
| 29 | +static InterfaceLogSettings make_eth_ifc_log_settings(const struct SilkwormRpcInterfaceLogSettings settings) { |
| 30 | + InterfaceLogSettings eth_ifc_log_settings{.ifc_name = "eth_rpc_api"}; |
| 31 | + eth_ifc_log_settings.enabled = settings.enabled; |
| 32 | + eth_ifc_log_settings.container_folder = parse_path(settings.container_folder); |
| 33 | + eth_ifc_log_settings.max_file_size_mb = settings.max_file_size_mb; |
| 34 | + eth_ifc_log_settings.max_files = settings.max_files; |
| 35 | + eth_ifc_log_settings.dump_response = settings.dump_response; |
| 36 | + return eth_ifc_log_settings; |
| 37 | +} |
| 38 | + |
| 39 | +//! Build JSON-RPC endpoint from C settings |
| 40 | +static std::string parse_end_point(const char (&c_host)[SILKWORM_RPC_SETTINGS_HOST_SIZE], int port, const std::string& default_end_point) { |
| 41 | + auto host = std::string{c_host}; |
| 42 | + if (host.empty() && port == 0) { |
| 43 | + return kDefaultEth1EndPoint; |
| 44 | + } |
| 45 | + const auto semicolon_position{default_end_point.find(':')}; |
| 46 | + SILKWORM_ASSERT(semicolon_position != std::string::npos); |
| 47 | + if (host.empty()) { |
| 48 | + host = default_end_point.substr(0, semicolon_position); |
| 49 | + } |
| 50 | + if (port == 0) { |
| 51 | + port = std::stoi(default_end_point.substr(semicolon_position + 1)); |
| 52 | + } |
| 53 | + std::string eth_end_point{host + ":" + std::to_string(port)}; |
| 54 | + return eth_end_point; |
| 55 | +} |
| 56 | + |
| 57 | +//! Build list of CORS domains from their C representation |
| 58 | +static std::vector<std::string> parse_cors_domains( |
| 59 | + const char (&c_cors_domains)[SILKWORM_RPC_SETTINGS_CORS_DOMAINS_MAX][SILKWORM_RPC_SETTINGS_CORS_DOMAIN_SIZE]) { |
| 60 | + std::vector<std::string> cors_domains; |
| 61 | + for (const auto& c_domain : c_cors_domains) { |
| 62 | + std::string_view domain_str = c_domain; |
| 63 | + if (domain_str.empty()) break; |
| 64 | + cors_domains.emplace_back(domain_str); |
| 65 | + } |
| 66 | + return cors_domains; |
| 67 | +} |
| 68 | + |
| 69 | +//! Build whole RPC daemon settings from their C representation |
| 70 | +static DaemonSettings make_daemon_settings(SilkwormHandle handle, const struct SilkwormRpcSettings& settings) { |
| 71 | + const auto jwt_path{parse_path(settings.jwt_file_path)}; |
| 72 | + return { |
| 73 | + .log_settings = handle->log_settings, |
| 74 | + .eth_ifc_log_settings = make_eth_ifc_log_settings(settings.eth_if_log_settings), |
| 75 | + .context_pool_settings = handle->context_pool_settings, |
| 76 | + .eth_end_point = parse_end_point(settings.eth_api_host, settings.eth_api_port, kDefaultEth1EndPoint), |
| 77 | + .engine_end_point = "", // disable end-point for Engine RPC API |
| 78 | + .eth_api_spec = std::string{settings.eth_api_spec}, |
| 79 | + .num_workers = settings.num_workers > 0 ? settings.num_workers : kDefaultNumWorkers, |
| 80 | + .cors_domain = parse_cors_domains(settings.cors_domains), |
| 81 | + .jwt_secret_file = jwt_path.empty() ? std::nullopt : std::make_optional(jwt_path.string()), |
| 82 | + .skip_protocol_check = settings.skip_internal_protocol_check, |
| 83 | + .erigon_json_rpc_compatibility = settings.erigon_json_rpc_compatibility, |
| 84 | + .use_websocket = settings.ws_enabled, |
| 85 | + .ws_compression = settings.ws_compression, |
| 86 | + .http_compression = settings.http_compression, |
| 87 | + }; |
| 88 | +} |
| 89 | + |
| 90 | +SILKWORM_EXPORT int silkworm_start_rpcdaemon(SilkwormHandle handle, MDBX_env* env, const struct SilkwormRpcSettings* settings) SILKWORM_NOEXCEPT { |
| 91 | + if (!handle) { |
| 92 | + return SILKWORM_INVALID_HANDLE; |
| 93 | + } |
| 94 | + if (handle->rpcdaemon) { |
| 95 | + return SILKWORM_SERVICE_ALREADY_STARTED; |
| 96 | + } |
| 97 | + if (!env) { |
| 98 | + return SILKWORM_INVALID_MDBX_ENV; |
| 99 | + } |
| 100 | + if (!settings) { |
| 101 | + return SILKWORM_INVALID_SETTINGS; |
| 102 | + } |
| 103 | + |
| 104 | + auto daemon_settings = make_daemon_settings(handle, *settings); |
| 105 | + db::EnvUnmanaged unmanaged_env{env}; |
| 106 | + |
| 107 | + // Create the one-and-only Silkrpc daemon |
| 108 | + handle->rpcdaemon = std::make_unique<rpc::Daemon>(daemon_settings, std::make_optional<mdbx::env>(unmanaged_env)); |
| 109 | + |
| 110 | + // Check protocol version compatibility with Core Services |
| 111 | + if (!daemon_settings.skip_protocol_check) { |
| 112 | + SILK_INFO << "[Silkworm RPC] Checking protocol version compatibility with core services..."; |
| 113 | + |
| 114 | + const auto checklist = handle->rpcdaemon->run_checklist(); |
| 115 | + for (const auto& protocol_check : checklist.protocol_checklist) { |
| 116 | + SILK_INFO << protocol_check.result; |
| 117 | + } |
| 118 | + checklist.success_or_throw(); |
| 119 | + } else { |
| 120 | + SILK_TRACE << "[Silkworm RPC] Skip protocol version compatibility check with core services"; |
| 121 | + } |
| 122 | + |
| 123 | + SILK_INFO << "[Silkworm RPC] Starting ETH API at " << daemon_settings.eth_end_point; |
| 124 | + try { |
| 125 | + handle->rpcdaemon->start(); |
| 126 | + } catch (const std::exception& ex) { |
| 127 | + SILK_ERROR << "[Silkworm RPC] Cannot start RPC daemon due to: " << ex.what(); |
| 128 | + return SILKWORM_INTERNAL_ERROR; |
| 129 | + } |
| 130 | + |
| 131 | + return SILKWORM_OK; |
| 132 | +} |
| 133 | + |
| 134 | +SILKWORM_EXPORT int silkworm_stop_rpcdaemon(SilkwormHandle handle) SILKWORM_NOEXCEPT { |
| 135 | + if (!handle) { |
| 136 | + return SILKWORM_INVALID_HANDLE; |
| 137 | + } |
| 138 | + if (!handle->rpcdaemon) { |
| 139 | + return SILKWORM_OK; |
| 140 | + } |
| 141 | + |
| 142 | + try { |
| 143 | + handle->rpcdaemon->stop(); |
| 144 | + SILK_INFO << "[Silkworm RPC] Exiting..."; |
| 145 | + handle->rpcdaemon->join(); |
| 146 | + SILK_INFO << "[Silkworm RPC] Stopped"; |
| 147 | + handle->rpcdaemon.reset(); |
| 148 | + } catch (const std::exception& ex) { |
| 149 | + SILK_ERROR << "[Silkworm RPC] Cannot stop RPC daemon due to: " << ex.what(); |
| 150 | + return SILKWORM_INTERNAL_ERROR; |
| 151 | + } |
| 152 | + |
| 153 | + return SILKWORM_OK; |
| 154 | +} |
0 commit comments