Skip to content

Commit

Permalink
capi: export RpcDaemon configuration settings (#1968)
Browse files Browse the repository at this point in the history
  • Loading branch information
canepat authored Apr 19, 2024
1 parent 3919a5f commit 66e333b
Show file tree
Hide file tree
Showing 11 changed files with 455 additions and 102 deletions.
3 changes: 2 additions & 1 deletion cmd/capi/execute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,8 @@ int start_rpcdaemon(SilkwormHandle handle, const rpc::DaemonSettings& /*settings
.exclusive = true};
::mdbx::env_managed env{silkworm::db::open_env(config)};

const int status_code{silkworm_start_rpcdaemon(handle, &*env)};
SilkwormRpcSettings settings{};
const int status_code{silkworm_start_rpcdaemon(handle, &*env, &settings)};
if (status_code != SILKWORM_OK) {
SILK_ERROR << "silkworm_start_rpcdaemon failed [code=" << std::to_string(status_code) << "]";
}
Expand Down
66 changes: 66 additions & 0 deletions silkworm/capi/common.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright 2024 The Silkworm Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "common.hpp"

#include <cstring>

namespace log = silkworm::log;

//! Build Silkworm log level from its C representation
static log::Level make_log_level(const SilkwormLogLevel c_log_level) {
log::Level verbosity{};
switch (c_log_level) {
case SilkwormLogLevel::NONE:
verbosity = log::Level::kNone;
break;
case SilkwormLogLevel::CRITICAL:
verbosity = log::Level::kCritical;
break;
case SilkwormLogLevel::ERROR:
verbosity = log::Level::kError;
break;
case SilkwormLogLevel::WARNING:
verbosity = log::Level::kWarning;
break;
case SilkwormLogLevel::INFO:
verbosity = log::Level::kInfo;
break;
case SilkwormLogLevel::DEBUG:
verbosity = log::Level::kDebug;
break;
case SilkwormLogLevel::TRACE:
verbosity = log::Level::kTrace;
break;
}
return verbosity;
}

std::filesystem::path parse_path(const char data_dir_path[SILKWORM_PATH_SIZE]) {
// Treat as char8_t so that filesystem::path assumes UTF-8 encoding of the input path
auto begin = reinterpret_cast<const char8_t*>(data_dir_path);
size_t len = strnlen(data_dir_path, SILKWORM_PATH_SIZE);
return std::filesystem::path{begin, begin + len};
}

log::Settings make_log_settings(const SilkwormLogLevel c_log_level) {
return {
.log_utc = false, // display local time
.log_timezone = false, // no timezone ID
.log_trim = true, // compact rendering (i.e. no whitespaces)
.log_verbosity = make_log_level(c_log_level),
};
}
29 changes: 29 additions & 0 deletions silkworm/capi/common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright 2024 The Silkworm Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#pragma once

#include <filesystem>

#include <silkworm/infra/common/log.hpp>

#include "silkworm.h"

//! Build a file system path from its C null-terminated upper-bounded representation
std::filesystem::path parse_path(const char path[SILKWORM_PATH_SIZE]);

//! Build log configuration matching Erigon log format w/ custom verbosity level
silkworm::log::Settings make_log_settings(SilkwormLogLevel c_log_level);
2 changes: 2 additions & 0 deletions silkworm/capi/instance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
#include <boost/asio/cancellation_signal.hpp>

#include <silkworm/db/snapshots/repository.hpp>
#include <silkworm/infra/common/log.hpp>
#include <silkworm/infra/concurrency/context_pool_settings.hpp>
#include <silkworm/rpc/daemon.hpp>

struct SilkwormInstance {
silkworm::log::Settings log_settings;
silkworm::concurrency::ContextPoolSettings context_pool_settings;
std::filesystem::path data_dir_path;
std::unique_ptr<silkworm::snapshots::SnapshotRepository> snapshot_repository;
Expand Down
154 changes: 154 additions & 0 deletions silkworm/capi/rpcdaemon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
Copyright 2024 The Silkworm Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <silkworm/infra/common/log.hpp>
#include <silkworm/rpc/settings.hpp>

#include "common.hpp"
#include "instance.hpp"
#include "silkworm.h"

using namespace silkworm;
using namespace silkworm::rpc;
using silkworm::concurrency::ContextPoolSettings;

//! Build interface log settings for ETH JSON-RPC from their C representation
static InterfaceLogSettings make_eth_ifc_log_settings(const struct SilkwormRpcInterfaceLogSettings settings) {
InterfaceLogSettings eth_ifc_log_settings{.ifc_name = "eth_rpc_api"};
eth_ifc_log_settings.enabled = settings.enabled;
eth_ifc_log_settings.container_folder = parse_path(settings.container_folder);
eth_ifc_log_settings.max_file_size_mb = settings.max_file_size_mb;
eth_ifc_log_settings.max_files = settings.max_files;
eth_ifc_log_settings.dump_response = settings.dump_response;
return eth_ifc_log_settings;
}

//! Build JSON-RPC endpoint from C settings
static std::string parse_end_point(const char (&c_host)[SILKWORM_RPC_SETTINGS_HOST_SIZE], int port, const std::string& default_end_point) {
auto host = std::string{c_host};
if (host.empty() && port == 0) {
return kDefaultEth1EndPoint;
}
const auto semicolon_position{default_end_point.find(':')};
SILKWORM_ASSERT(semicolon_position != std::string::npos);
if (host.empty()) {
host = default_end_point.substr(0, semicolon_position);
}
if (port == 0) {
port = std::stoi(default_end_point.substr(semicolon_position + 1));
}
std::string eth_end_point{host + ":" + std::to_string(port)};
return eth_end_point;
}

//! Build list of CORS domains from their C representation
static std::vector<std::string> parse_cors_domains(
const char (&c_cors_domains)[SILKWORM_RPC_SETTINGS_CORS_DOMAINS_MAX][SILKWORM_RPC_SETTINGS_CORS_DOMAIN_SIZE]) {
std::vector<std::string> cors_domains;
for (const auto& c_domain : c_cors_domains) {
std::string_view domain_str = c_domain;
if (domain_str.empty()) break;
cors_domains.emplace_back(domain_str);
}
return cors_domains;
}

//! Build whole RPC daemon settings from their C representation
static DaemonSettings make_daemon_settings(SilkwormHandle handle, const struct SilkwormRpcSettings& settings) {
const auto jwt_path{parse_path(settings.jwt_file_path)};
return {
.log_settings = handle->log_settings,
.eth_ifc_log_settings = make_eth_ifc_log_settings(settings.eth_if_log_settings),
.context_pool_settings = handle->context_pool_settings,
.eth_end_point = parse_end_point(settings.eth_api_host, settings.eth_api_port, kDefaultEth1EndPoint),
.engine_end_point = "", // disable end-point for Engine RPC API
.eth_api_spec = std::string{settings.eth_api_spec},
.num_workers = settings.num_workers > 0 ? settings.num_workers : kDefaultNumWorkers,
.cors_domain = parse_cors_domains(settings.cors_domains),
.jwt_secret_file = jwt_path.empty() ? std::nullopt : std::make_optional(jwt_path.string()),
.skip_protocol_check = settings.skip_internal_protocol_check,
.erigon_json_rpc_compatibility = settings.erigon_json_rpc_compatibility,
.use_websocket = settings.ws_enabled,
.ws_compression = settings.ws_compression,
.http_compression = settings.http_compression,
};
}

SILKWORM_EXPORT int silkworm_start_rpcdaemon(SilkwormHandle handle, MDBX_env* env, const struct SilkwormRpcSettings* settings) SILKWORM_NOEXCEPT {
if (!handle) {
return SILKWORM_INVALID_HANDLE;
}
if (handle->rpcdaemon) {
return SILKWORM_SERVICE_ALREADY_STARTED;
}
if (!env) {
return SILKWORM_INVALID_MDBX_ENV;
}
if (!settings) {
return SILKWORM_INVALID_SETTINGS;
}

auto daemon_settings = make_daemon_settings(handle, *settings);
db::EnvUnmanaged unmanaged_env{env};

// Create the one-and-only Silkrpc daemon
handle->rpcdaemon = std::make_unique<rpc::Daemon>(daemon_settings, std::make_optional<mdbx::env>(unmanaged_env));

// Check protocol version compatibility with Core Services
if (!daemon_settings.skip_protocol_check) {
SILK_INFO << "[Silkworm RPC] Checking protocol version compatibility with core services...";

const auto checklist = handle->rpcdaemon->run_checklist();
for (const auto& protocol_check : checklist.protocol_checklist) {
SILK_INFO << protocol_check.result;
}
checklist.success_or_throw();
} else {
SILK_TRACE << "[Silkworm RPC] Skip protocol version compatibility check with core services";
}

SILK_INFO << "[Silkworm RPC] Starting ETH API at " << daemon_settings.eth_end_point;
try {
handle->rpcdaemon->start();
} catch (const std::exception& ex) {
SILK_ERROR << "[Silkworm RPC] Cannot start RPC daemon due to: " << ex.what();
return SILKWORM_INTERNAL_ERROR;
}

return SILKWORM_OK;
}

SILKWORM_EXPORT int silkworm_stop_rpcdaemon(SilkwormHandle handle) SILKWORM_NOEXCEPT {
if (!handle) {
return SILKWORM_INVALID_HANDLE;
}
if (!handle->rpcdaemon) {
return SILKWORM_OK;
}

try {
handle->rpcdaemon->stop();
SILK_INFO << "[Silkworm RPC] Exiting...";
handle->rpcdaemon->join();
SILK_INFO << "[Silkworm RPC] Stopped";
handle->rpcdaemon.reset();
} catch (const std::exception& ex) {
SILK_ERROR << "[Silkworm RPC] Cannot stop RPC daemon due to: " << ex.what();
return SILKWORM_INTERNAL_ERROR;
}

return SILKWORM_OK;
}
Loading

0 comments on commit 66e333b

Please sign in to comment.