Skip to content

Commit 66e333b

Browse files
authored
capi: export RpcDaemon configuration settings (#1968)
1 parent 3919a5f commit 66e333b

File tree

11 files changed

+455
-102
lines changed

11 files changed

+455
-102
lines changed

cmd/capi/execute.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,8 @@ int start_rpcdaemon(SilkwormHandle handle, const rpc::DaemonSettings& /*settings
396396
.exclusive = true};
397397
::mdbx::env_managed env{silkworm::db::open_env(config)};
398398

399-
const int status_code{silkworm_start_rpcdaemon(handle, &*env)};
399+
SilkwormRpcSettings settings{};
400+
const int status_code{silkworm_start_rpcdaemon(handle, &*env, &settings)};
400401
if (status_code != SILKWORM_OK) {
401402
SILK_ERROR << "silkworm_start_rpcdaemon failed [code=" << std::to_string(status_code) << "]";
402403
}

silkworm/capi/common.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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 "common.hpp"
18+
19+
#include <cstring>
20+
21+
namespace log = silkworm::log;
22+
23+
//! Build Silkworm log level from its C representation
24+
static log::Level make_log_level(const SilkwormLogLevel c_log_level) {
25+
log::Level verbosity{};
26+
switch (c_log_level) {
27+
case SilkwormLogLevel::NONE:
28+
verbosity = log::Level::kNone;
29+
break;
30+
case SilkwormLogLevel::CRITICAL:
31+
verbosity = log::Level::kCritical;
32+
break;
33+
case SilkwormLogLevel::ERROR:
34+
verbosity = log::Level::kError;
35+
break;
36+
case SilkwormLogLevel::WARNING:
37+
verbosity = log::Level::kWarning;
38+
break;
39+
case SilkwormLogLevel::INFO:
40+
verbosity = log::Level::kInfo;
41+
break;
42+
case SilkwormLogLevel::DEBUG:
43+
verbosity = log::Level::kDebug;
44+
break;
45+
case SilkwormLogLevel::TRACE:
46+
verbosity = log::Level::kTrace;
47+
break;
48+
}
49+
return verbosity;
50+
}
51+
52+
std::filesystem::path parse_path(const char data_dir_path[SILKWORM_PATH_SIZE]) {
53+
// Treat as char8_t so that filesystem::path assumes UTF-8 encoding of the input path
54+
auto begin = reinterpret_cast<const char8_t*>(data_dir_path);
55+
size_t len = strnlen(data_dir_path, SILKWORM_PATH_SIZE);
56+
return std::filesystem::path{begin, begin + len};
57+
}
58+
59+
log::Settings make_log_settings(const SilkwormLogLevel c_log_level) {
60+
return {
61+
.log_utc = false, // display local time
62+
.log_timezone = false, // no timezone ID
63+
.log_trim = true, // compact rendering (i.e. no whitespaces)
64+
.log_verbosity = make_log_level(c_log_level),
65+
};
66+
}

silkworm/capi/common.hpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
#pragma once
18+
19+
#include <filesystem>
20+
21+
#include <silkworm/infra/common/log.hpp>
22+
23+
#include "silkworm.h"
24+
25+
//! Build a file system path from its C null-terminated upper-bounded representation
26+
std::filesystem::path parse_path(const char path[SILKWORM_PATH_SIZE]);
27+
28+
//! Build log configuration matching Erigon log format w/ custom verbosity level
29+
silkworm::log::Settings make_log_settings(SilkwormLogLevel c_log_level);

silkworm/capi/instance.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
#include <boost/asio/cancellation_signal.hpp>
2424

2525
#include <silkworm/db/snapshots/repository.hpp>
26+
#include <silkworm/infra/common/log.hpp>
2627
#include <silkworm/infra/concurrency/context_pool_settings.hpp>
2728
#include <silkworm/rpc/daemon.hpp>
2829

2930
struct SilkwormInstance {
31+
silkworm::log::Settings log_settings;
3032
silkworm::concurrency::ContextPoolSettings context_pool_settings;
3133
std::filesystem::path data_dir_path;
3234
std::unique_ptr<silkworm::snapshots::SnapshotRepository> snapshot_repository;

silkworm/capi/rpcdaemon.cpp

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
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

Comments
 (0)