RPC server is moved to a new process (optionally) (#1874)

* Move RPC to new process

* Readd reuse address

* Formatting

* Remove rpc dependency from node CMakeLists.txt file

* Fix rpc.online_reps test

* Only use boost process when using a boost version other than 1.69

* Formatting, add thread name to RPC thread

* Call lock () on the lock itself not mutex

* Allow RPC in-process as well

* Formatting

* Formatting

* Revert filenames to have underscores again for readability

* Use boost interprocess with node daemon as well. Move definitions to CMakeLists.txt

* Delete unused options after migration. Don't migrate version so that an upgrade to the new rpc_config is performed filling in the rest of the options with appropriate defaults.

* Change debug_ipc to debug_rpc

* Removed unused ipc_path from new rpc_config

* Change --daemon CLI help to state it is for the RPC

* Remove rpc_path from load-tester

* Fix global network_constants initialization issue

* Fix session timer timing issue

* Remove default_ipc_port from node_rpc_config::migrate ()

* Remove semi-colon from command line args preventing others below it from working.

* Remove message about failing to migrate RPC path in rpc_config

* Move cleanup_test_directories_on_exit to node to resolve link error with nano_rpc
This commit is contained in:
Wesley Shillingford 2019-04-21 17:41:36 +01:00 committed by GitHub
commit c3046c11d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
73 changed files with 7551 additions and 5749 deletions

6
.gitignore vendored
View file

@ -32,9 +32,15 @@ core_test
rpc_test
!rpc_test/
qt_test
!qt_test/
nano_node
!nano_node/
nano_wallet
!nano_wallet/
slow_test
!slow_test/
nano_rpc
!nano_rpc/
# IDEs
.idea

View file

@ -157,6 +157,13 @@ endif ()
find_package (Boost 1.67.0 REQUIRED COMPONENTS filesystem log thread program_options system)
# There is a compile bug with boost 1.69 interprocess headers on Mac
if (APPLE AND Boost_VERSION EQUAL 106900)
set (BOOST_PROCESS_SUPPORTED 0)
else ()
set (BOOST_PROCESS_SUPPORTED 1)
endif ()
add_subdirectory(crypto/ed25519-donna)
set (UPNPC_BUILD_SHARED OFF CACHE BOOL "")
@ -292,8 +299,9 @@ add_subdirectory(nano/crypto_lib)
add_subdirectory(nano/secure)
add_subdirectory(nano/lib)
add_subdirectory(nano/node)
add_subdirectory(nano/rpc)
add_subdirectory(nano/nano_node)
add_subdirectory(nano/rpc)
add_subdirectory(nano/nano_rpc)
if (NANO_TEST OR RAIBLOCKS_TEST)
if(WIN32)
@ -369,9 +377,14 @@ if (NANO_GUI OR RAIBLOCKS_GUI)
${RES})
target_link_libraries (nano_wallet
rpc
node
qt)
target_compile_definitions(nano_wallet
PRIVATE
-DBOOST_PROCESS_SUPPORTED=${BOOST_PROCESS_SUPPORTED})
if (WIN32)
target_link_libraries (nano_wallet Qt5::WinExtras)
# nano_wallet.com executable for Windows console

View file

@ -71,7 +71,7 @@ run_tests() {
xvfb_run_ ./qt_test
qt_test_res=${?}
${TIMEOUT_CMD} ${TIMEOUT_TIME_ARG} ${TIMEOUT_SEC-${TIMEOUT_DEFAULT}} ./load_test ./nano_node -s 150
${TIMEOUT_CMD} ${TIMEOUT_TIME_ARG} ${TIMEOUT_SEC-${TIMEOUT_DEFAULT}} ./load_test ./nano_node ./nano_rpc -s 150
load_test_res=${?}
echo "Core Test return code: ${core_test_res}"

View file

@ -1,125 +0,0 @@
use std::io;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::process::Command;
use serde_json;
use futures::Future;
use hyper::client::Connect;
use tokio_core::reactor::Handle;
use tokio_process::{Child, CommandExt};
use serde_json::Value;
use errors::*;
use rpc::RpcClient;
const RPC_PORT_START: u64 = 55000;
const PEERING_PORT_START: u64 = 54000;
pub fn launch_node(
nano_node: &Path,
tmp_dir: &Path,
handle: Handle,
i: u64,
) -> Result<(Child, RpcClient)> {
let data_dir = tmp_dir.join(format!("Nano_load_test_{}", i));
match fs::create_dir(&data_dir) {
Ok(_) => {}
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => {
let _ = fs::remove_file(data_dir.join("data.ldb"));
}
r => r.chain_err(|| "failed to create nano_node data directory")?,
}
let rpc_port = RPC_PORT_START + i;
let peering_port = PEERING_PORT_START + i;
let config = json!({
"version": "2",
"rpc_enable": "true",
"rpc": {
"address": "::1",
"port": rpc_port.to_string(),
"enable_control": "true",
"frontier_request_limit": "16384",
"chain_request_limit": "16384",
},
"node": {
"version": "8",
"peering_port": peering_port.to_string(),
"bootstrap_fraction_numerator": "1",
"receive_minimum": "1000000000000000000000000",
"logging": {
"version": "2",
"ledger": "false",
"ledger_duplicate": "false",
"vote": "false",
"network": "true",
"network_message": "false",
"network_publish": "false",
"network_packet": "false",
"network_keepalive": "false",
"node_lifetime_tracing": "false",
"insufficient_work": "true",
"log_rpc": "true",
"bulk_pull": "false",
"work_generation_time": "true",
"log_to_cerr": "false",
"max_size": "16777216",
},
"work_peers": "",
"preconfigured_peers": "",
"preconfigured_representatives": [
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo"
],
"inactive_supply": "0",
"password_fanout": "1024",
"io_threads": "8",
"work_threads": "8",
"enable_voting": "true",
"bootstrap_connections": "4",
"callback_address": "",
"callback_port": "0",
"callback_target": "",
"lmdb_max_dbs": "128",
},
"opencl_enable": "false",
"opencl": {
"platform": "0",
"device": "0",
"threads": "1048576",
}
});
let config_writer =
File::create(data_dir.join("config.json")).chain_err(|| "failed to create config.json")?;
serde_json::to_writer_pretty(config_writer, &config)
.chain_err(|| "failed to write config.json")?;
let child = Command::new(nano_node)
.arg("--data_path")
.arg(&data_dir)
.arg("--daemon")
.spawn_async(&handle)
.chain_err(|| "failed to spawn nano_node")?;
let rpc_client = RpcClient::new(
handle,
format!("http://[::1]:{}/", rpc_port).parse().unwrap(),
);
Ok((child, rpc_client))
}
pub fn connect_node<C: Connect>(
node: &RpcClient<C>,
i: u64,
) -> Box<Future<Item = (), Error = Error>> {
Box::new(
node.call::<_, Value>(&json!({
"action": "keepalive",
"address": "::1",
"port": PEERING_PORT_START + i,
})).then(|x| x.chain_err(|| "failed to call nano_node RPC"))
.map(|_| ()),
) as _
}

View file

@ -0,0 +1,181 @@
use std::io;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::process::Command;
use serde_json;
use futures::Future;
use hyper::client::Connect;
use tokio_core::reactor::Handle;
use tokio_process::{Child, CommandExt};
use serde_json::Value;
use errors::*;
use rpc::RpcClient;
const RPC_PORT_START: u64 = 55000;
const PEERING_PORT_START: u64 = 54000;
const IPC_PORT_START: u64 = 56000;
pub fn launch_node_and_rpc(
nano_node: &Path,
nano_rpc: &Path,
tmp_dir: &Path,
handle: Handle,
i: u64,
) -> Result<(Child, Child, RpcClient)> {
let data_dir = tmp_dir.join(format!("Nano_load_test_{}", i));
match fs::create_dir(&data_dir) {
Ok(_) => {}
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => {
let _ = fs::remove_file(data_dir.join("data.ldb"));
let _ = fs::remove_file(data_dir.join("wallets.ldb"));
}
r => r.chain_err(|| "failed to create nano_node data directory")?,
}
let peering_port = PEERING_PORT_START + i;
let ipc_port = IPC_PORT_START + i;
let config = json!({
"version": "2",
"rpc_enable": "false",
"rpc": {
"version": "1",
"enable_sign_hash": "false",
"max_work_generate_difficulty": "ffffffffc0000000",
"rpc_in_process": "false"
},
"node": {
"version": "17",
"peering_port": peering_port.to_string(),
"bootstrap_fraction_numerator": "1",
"receive_minimum": "1000000000000000000000000",
"logging": {
"version": "7",
"ledger": "false",
"ledger_duplicate": "false",
"vote": "false",
"network": "true",
"network_message": "false",
"network_publish": "false",
"network_packet": "false",
"network_keepalive": "false",
"network_node_id_handshake": "false",
"node_lifetime_tracing": "false",
"insufficient_work": "true",
"log_ipc": "true",
"bulk_pull": "false",
"work_generation_time": "true",
"upnp_details": "false",
"timing": "false",
"log_to_cerr": "false",
"max_size": "134217728",
"rotation_size": "4194304",
"flush": "true",
"min_time_between_output": "5"
},
"work_peers": "",
"preconfigured_peers": "",
"preconfigured_representatives": [
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo"
],
"online_weight_minimum": "60000000000000000000000000000000000000",
"online_weight_quorum": "50",
"password_fanout": "1024",
"io_threads": "8",
"network_threads": "8",
"work_threads": "8",
"signature_checker_threads": "7",
"enable_voting": "true",
"bootstrap_connections": "4",
"bootstrap_connections_max": "64",
"callback_address": "",
"callback_port": "0",
"callback_target": "",
"lmdb_max_dbs": "128",
"block_processor_batch_max_time": "5000",
"allow_local_peers": "true",
"vote_minimum": "1000000000000000000000000000000000",
"unchecked_cutoff_time": "14400",
"ipc": {
"tcp": {
"enable": "true",
"port": ipc_port.to_string (),
"io_timeout": "15"
},
"local": {
"enable": "false",
"path": "/tmp/nano",
"io_timeout": "15"
},
},
"tcp_client_timeout": "5",
"tcp_server_timeout": "30"
},
"opencl_enable": "false",
"opencl": {
"platform": "0",
"device": "0",
"threads": "1048576"
}
});
let rpc_port = RPC_PORT_START + i;
let rpc_config = json!({
"address": "::1",
"port": rpc_port.to_string(),
"enable_control": "true",
"max_json_depth": "20",
"version": "1",
"ipc_port": ipc_port.to_string (),
"io_threads": "8",
"num_ipc_connections" : "8"
});
let config_writer =
File::create(data_dir.join("config.json")).chain_err(|| "failed to create config.json")?;
serde_json::to_writer_pretty(config_writer, &config)
.chain_err(|| "failed to write config.json")?;
let child = Command::new(nano_node)
.arg("--data_path")
.arg(&data_dir)
.arg("--daemon")
.spawn_async(&handle)
.chain_err(|| "failed to spawn nano_node")?;
let rpc_config_writer =
File::create(data_dir.join("rpc_config.json")).chain_err(|| "failed to create rpc_config.json")?;
serde_json::to_writer_pretty(rpc_config_writer, &rpc_config)
.chain_err(|| "failed to write rpc_config.json")?;
let rpc_child = Command::new(nano_rpc)
.arg("--data_path")
.arg(&data_dir)
.arg("--daemon")
.spawn_async(&handle)
.chain_err(|| "failed to spawn nano_rpc")?;
let rpc_client = RpcClient::new(
handle,
format!("http://[::1]:{}/", rpc_port).parse().unwrap(),
);
Ok((child, rpc_child, rpc_client))
}
pub fn connect_node<C: Connect>(
node: &RpcClient<C>,
i: u64,
) -> Box<Future<Item = (), Error = Error>> {
Box::new(
node.call::<_, Value>(&json!({
"action": "keepalive",
"address": "::1",
"port": PEERING_PORT_START + i,
})).then(|x| x.chain_err(|| "failed to call nano_rpc"))
.map(|_| ()),
) as _
}

View file

@ -46,11 +46,12 @@ use errors::*;
mod rpc;
use rpc::{RpcClient, RpcError};
mod launch_node;
mod launch_node_and_rpc;
struct Parameters {
node_count: u16,
node_path: PathBuf,
rpc_path: PathBuf,
tmp_dir: PathBuf,
send_count: usize,
dest_count: usize,
@ -82,15 +83,18 @@ fn run(params: Parameters) -> Result<()> {
let mut tokio_core = Core::new().chain_err(|| "failed to create tokio Core")?;
let mut children = Vec::with_capacity(params.node_count as _);
let mut rpc_children = Vec::with_capacity(params.node_count as _);
let mut nodes: Vec<RpcClient<_>> = Vec::with_capacity(params.node_count as _);
for i in 0..params.node_count {
let (child, rpc_client) = launch_node::launch_node(
let (child, rpc_child, rpc_client) = launch_node_and_rpc::launch_node_and_rpc(
&params.node_path,
&params.rpc_path,
&params.tmp_dir,
tokio_core.handle(),
i as _,
)?;
children.push(child);
rpc_children.push(rpc_child);
nodes.push(rpc_client);
}
if nodes.is_empty() {
@ -103,7 +107,7 @@ fn run(params: Parameters) -> Result<()> {
for (a, node) in nodes.iter().enumerate() {
for b in 0..nodes.len() {
if a != b {
tokio_core.run(launch_node::connect_node(node, b as _))?;
tokio_core.run(launch_node_and_rpc::connect_node(node, b as _))?;
}
}
}
@ -140,7 +144,7 @@ fn run(params: Parameters) -> Result<()> {
"action": "key_create",
}))
})
.buffer_unordered(10) // execute 10 `key_create`s simultaniously
.buffer_unordered(10) // execute 10 `key_create`s simultaneously
.inspect(|_| {
tstat!("key_create,progress");
})
@ -460,6 +464,12 @@ fn main() {
.required(true)
.help("The path to the nano_node to test"),
)
.arg(
Arg::with_name("rpc_path")
.value_name("PATH")
.required(true)
.help("The path to the nano_rpc to test"),
)
.arg(
Arg::with_name("send_count")
.short("s")
@ -520,6 +530,7 @@ fn main() {
let params = Parameters {
node_count: num_arg!("node_count"),
node_path: matches.value_of("node_path").unwrap().into(),
rpc_path: matches.value_of("rpc_path").unwrap().into(),
tmp_dir: matches
.value_of("tmp_dir")
.or(env::var("TMPDIR").ok().as_ref().map(|x| x.as_str()))

View file

@ -3,11 +3,11 @@ add_executable (core_test
testutil.hpp
block.cpp
block_store.cpp
interface.cpp
ipc.cpp
conflicts.cpp
entry.cpp
gap_cache.cpp
interface.cpp
ipc.cpp
ledger.cpp
logger.cpp
network.cpp

View file

@ -1,7 +1,7 @@
#include "gtest/gtest.h"
#include <nano/secure/utility.hpp>
namespace nano
{
void cleanup_test_directories_on_exit ();
void force_nano_test_network ();
}
GTEST_API_ int main (int argc, char ** argv)
@ -10,6 +10,6 @@ GTEST_API_ int main (int argc, char ** argv)
nano::force_nano_test_network ();
testing::InitGoogleTest (&argc, argv);
auto res = RUN_ALL_TESTS ();
nano::cleanp_test_directories_on_exit ();
nano::cleanup_test_directories_on_exit ();
return res;
}

View file

@ -1,5 +0,0 @@
#include <gtest/gtest.h>
TEST (daemon, fork)
{
}

View file

@ -15,10 +15,9 @@ using namespace std::chrono_literals;
TEST (ipc, asynchronous)
{
nano::system system (24000, 1);
nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true));
system.nodes[0]->config.ipc_config.transport_tcp.enabled = true;
system.nodes[0]->config.ipc_config.transport_tcp.port = 24077;
nano::ipc::ipc_server ipc (*system.nodes[0], rpc);
nano::ipc::ipc_server ipc (*system.nodes[0]);
nano::ipc::ipc_client client (system.nodes[0]->io_ctx);
auto req (nano::ipc::prepare_request (nano::ipc::payload_encoding::json_legacy, std::string (R"({"action": "block_count"})")));
@ -58,10 +57,9 @@ TEST (ipc, asynchronous)
TEST (ipc, synchronous)
{
nano::system system (24000, 1);
nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true));
system.nodes[0]->config.ipc_config.transport_tcp.enabled = true;
system.nodes[0]->config.ipc_config.transport_tcp.port = 24077;
nano::ipc::ipc_server ipc (*system.nodes[0], rpc);
nano::ipc::ipc_server ipc (*system.nodes[0]);
nano::ipc::ipc_client client (system.nodes[0]->io_ctx);
// Start blocking IPC client in a separate thread

View file

@ -22,7 +22,6 @@ TEST (logging, serialization)
logging1.network_node_id_handshake_logging_value = !logging1.network_node_id_handshake_logging_value;
logging1.node_lifetime_tracing_value = !logging1.node_lifetime_tracing_value;
logging1.insufficient_work_logging_value = !logging1.insufficient_work_logging_value;
logging1.log_rpc_value = !logging1.log_rpc_value;
logging1.bulk_pull_logging_value = !logging1.bulk_pull_logging_value;
logging1.work_generation_time_value = !logging1.work_generation_time_value;
logging1.log_to_cerr_value = !logging1.log_to_cerr_value;
@ -45,7 +44,6 @@ TEST (logging, serialization)
ASSERT_EQ (logging1.network_node_id_handshake_logging_value, logging2.network_node_id_handshake_logging_value);
ASSERT_EQ (logging1.node_lifetime_tracing_value, logging2.node_lifetime_tracing_value);
ASSERT_EQ (logging1.insufficient_work_logging_value, logging2.insufficient_work_logging_value);
ASSERT_EQ (logging1.log_rpc_value, logging2.log_rpc_value);
ASSERT_EQ (logging1.bulk_pull_logging_value, logging2.bulk_pull_logging_value);
ASSERT_EQ (logging1.work_generation_time_value, logging2.work_generation_time_value);
ASSERT_EQ (logging1.log_to_cerr_value, logging2.log_to_cerr_value);

View file

@ -27,8 +27,10 @@ add_library (nano_lib
ipc.cpp
ipc_client.hpp
ipc_client.cpp
json_error_response.hpp
jsonconfig.hpp
logger_mt.hpp
rpc_handler_interface.hpp
rpcconfig.hpp
rpcconfig.cpp
numbers.hpp

View file

@ -132,6 +132,11 @@ inline boost::filesystem::path get_config_path (boost::filesystem::path const &
return data_path / "config.json";
}
inline boost::filesystem::path get_rpc_config_path (boost::filesystem::path const & data_path)
{
return data_path / "rpc_config.json";
}
/** Called by gtest_main to enforce test network */
void force_nano_test_network ();
}

View file

@ -0,0 +1,16 @@
#pragma once
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
namespace nano
{
inline void json_error_response (std::function<void(std::string const &)> response_a, std::string const & message_a)
{
boost::property_tree::ptree response_l;
response_l.put ("error", message_a);
std::stringstream ostream;
boost::property_tree::write_json (ostream, response_l);
response_a (ostream.str ());
}
}

View file

@ -0,0 +1,15 @@
#pragma once
namespace nano
{
class rpc;
class rpc_handler_interface
{
public:
virtual ~rpc_handler_interface () = default;
virtual void process_request (std::string const & action, std::string const & body, std::function<void(std::string const &)> response) = 0;
virtual void stop () = 0;
virtual void rpc_instance (nano::rpc & rpc) = 0;
};
}

View file

@ -1,3 +1,4 @@
#include <boost/dll/runtime_symbol_info.hpp>
#include <nano/lib/config.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/rpcconfig.hpp>
@ -31,9 +32,11 @@ address (boost::asio::ip::address_v6::loopback ()),
port (network_constants.default_rpc_port),
enable_control (enable_control_a),
max_json_depth (20),
enable_sign_hash (false),
max_request_size (32 * 1024 * 1024),
max_work_generate_difficulty (0xffffffffc0000000)
io_threads (std::max<unsigned> (4, boost::thread::hardware_concurrency ())),
ipc_port (network_constants.default_ipc_port),
ipc_path ("/tmp/nano"),
num_ipc_connections (network_constants.is_live_network () ? 8 : 1)
{
}
@ -44,44 +47,79 @@ nano::error nano::rpc_config::serialize_json (nano::jsonconfig & json) const
json.put ("port", port);
json.put ("enable_control", enable_control);
json.put ("max_json_depth", max_json_depth);
json.put ("enable_sign_hash", enable_sign_hash);
json.put ("max_request_size", max_request_size);
json.put ("max_work_generate_difficulty", nano::to_string_hex (max_work_generate_difficulty));
json.put ("io_threads", io_threads);
json.put ("ipc_port", ipc_port);
json.put ("num_ipc_connections", num_ipc_connections);
return json.get_error ();
}
nano::error nano::rpc_config::deserialize_json (bool & upgraded_a, nano::jsonconfig & json)
{
auto version_l (json.get_optional<unsigned> ("version"));
if (!version_l)
if (!json.empty ())
{
version_l = 1;
json.put ("version", *version_l);
json.put ("max_request_size", max_request_size);
json.put ("max_work_generate_difficulty", nano::to_string_hex (max_work_generate_difficulty));
json.erase ("frontier_request_limit");
json.erase ("chain_request_limit");
auto version_l (json.get_optional<unsigned> ("version"));
if (!version_l)
{
version_l = 1;
json.put ("version", *version_l);
json.put ("max_request_size", max_request_size);
json.erase ("frontier_request_limit");
json.erase ("chain_request_limit");
json.put ("io_threads", io_threads);
json.put ("ipc_port", ipc_port);
json.put ("num_ipc_connections", num_ipc_connections);
upgraded_a = true;
}
auto rpc_secure_l (json.get_optional_child ("secure"));
if (rpc_secure_l)
{
secure.deserialize_json (*rpc_secure_l);
}
json.get_required<boost::asio::ip::address_v6> ("address", address);
json.get_optional<uint16_t> ("port", port);
json.get_optional<bool> ("enable_control", enable_control);
json.get_optional<uint8_t> ("max_json_depth", max_json_depth);
json.get_optional<uint64_t> ("max_request_size", max_request_size);
json.get_optional<unsigned> ("io_threads", io_threads);
json.get_optional<uint16_t> ("ipc_port", ipc_port);
json.get_optional<unsigned> ("num_ipc_connections", num_ipc_connections);
}
else
{
upgraded_a = true;
serialize_json (json);
}
auto rpc_secure_l (json.get_optional_child ("secure"));
if (rpc_secure_l)
{
secure.deserialize_json (*rpc_secure_l);
}
json.get_required<boost::asio::ip::address_v6> ("address", address);
json.get_optional<uint16_t> ("port", port);
json.get_optional<bool> ("enable_control", enable_control);
json.get_optional<uint8_t> ("max_json_depth", max_json_depth);
json.get_optional<bool> ("enable_sign_hash", enable_sign_hash);
json.get_optional<uint64_t> ("max_request_size", max_request_size);
std::string max_work_generate_difficulty_text;
json.get_optional<std::string> ("max_work_generate_difficulty", max_work_generate_difficulty_text);
if (!max_work_generate_difficulty_text.empty ())
{
nano::from_string_hex (max_work_generate_difficulty_text, max_work_generate_difficulty);
}
return json.get_error ();
}
namespace nano
{
nano::error read_and_update_rpc_config (boost::filesystem::path const & data_path, nano::rpc_config & config_a)
{
boost::system::error_code error_chmod;
nano::jsonconfig json;
auto config_path = nano::get_rpc_config_path (data_path);
auto error (json.read_and_update (config_a, config_path));
nano::set_secure_perm_file (config_path, error_chmod);
return error;
}
std::string get_default_rpc_filepath ()
{
boost::system::error_code err;
auto running_executable_filepath = boost::dll::program_location (err);
// Construct the nano_rpc excutable file path based on where the currently running executable is found.
auto rpc_filepath = running_executable_filepath.parent_path () / "nano_rpc";
if (running_executable_filepath.has_extension ())
{
rpc_filepath.replace_extension (running_executable_filepath.extension ());
}
return rpc_filepath.string ();
}
}

View file

@ -1,8 +1,9 @@
#pragma once
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
#include <nano/lib/config.hpp>
#include <nano/lib/errors.hpp>
#include <nano/secure/common.hpp>
#include <string>
namespace nano
@ -44,12 +45,18 @@ public:
bool enable_control;
rpc_secure_config secure;
uint8_t max_json_depth;
bool enable_sign_hash;
uint64_t max_request_size;
uint64_t max_work_generate_difficulty;
unsigned io_threads;
uint16_t ipc_port;
std::string ipc_path;
unsigned num_ipc_connections;
static int json_version ()
{
return 1;
}
};
nano::error read_and_update_rpc_config (boost::filesystem::path const & data_path, nano::rpc_config & config_a);
std::string get_default_rpc_filepath ();
}

View file

@ -96,6 +96,12 @@ namespace thread_role
case nano::thread_role::name::signature_checking:
thread_role_name_string = "Signature check";
break;
case nano::thread_role::name::rpc_request_processor:
thread_role_name_string = "RPC processor";
break;
case nano::thread_role::name::rpc_process_container:
thread_role_name_string = "RPC process";
break;
}
/*

View file

@ -85,7 +85,9 @@ namespace thread_role
wallet_actions,
bootstrap_initiator,
voting,
signature_checking
signature_checking,
rpc_request_processor,
rpc_process_container
};
/*
* Get/Set the identifier for the current thread

View file

@ -5,6 +5,7 @@ add_executable (nano_node
target_link_libraries (nano_node
node
rpc
secure
argon2
Boost::boost
@ -15,7 +16,8 @@ target_compile_definitions(nano_node
PRIVATE
-DNANO_VERSION_MAJOR=${CPACK_PACKAGE_VERSION_MAJOR}
-DNANO_VERSION_MINOR=${CPACK_PACKAGE_VERSION_MINOR}
-DNANO_VERSION_PATCH=${CPACK_PACKAGE_VERSION_PATCH})
-DNANO_VERSION_PATCH=${CPACK_PACKAGE_VERSION_PATCH}
-DBOOST_PROCESS_SUPPORTED=${BOOST_PROCESS_SUPPORTED})
set_target_properties (nano_node
PROPERTIES

View file

@ -5,7 +5,19 @@
#include <nano/nano_node/daemon.hpp>
#include <nano/node/daemonconfig.hpp>
#include <nano/node/ipc.hpp>
#include <nano/node/json_handler.hpp>
#include <nano/node/node.hpp>
#include <nano/node/openclwork.hpp>
#include <nano/node/working.hpp>
#include <nano/rpc/rpc.hpp>
#ifndef BOOST_PROCESS_SUPPORTED
#error BOOST_PROCESS_SUPPORTED must be set, check configuration
#endif
#if BOOST_PROCESS_SUPPORTED
#include <boost/process.hpp>
#endif
void nano_daemon::daemon::run (boost::filesystem::path const & data_path, nano::node_flags const & flags)
{
@ -13,7 +25,7 @@ void nano_daemon::daemon::run (boost::filesystem::path const & data_path, nano::
boost::system::error_code error_chmod;
nano::set_secure_perm_directory (data_path, error_chmod);
std::unique_ptr<nano::thread_runner> runner;
nano::daemon_config config;
nano::daemon_config config (data_path);
auto error = nano::read_and_update_daemon_config (data_path, config);
if (!error)
@ -33,14 +45,57 @@ void nano_daemon::daemon::run (boost::filesystem::path const & data_path, nano::
if (!init.error ())
{
node->start ();
std::unique_ptr<nano::rpc> rpc = get_rpc (io_ctx, *node, config.rpc);
if (rpc)
nano::ipc::ipc_server ipc_server (*node, config.rpc);
#if BOOST_PROCESS_SUPPORTED
std::unique_ptr<boost::process::child> rpc_process;
#endif
std::unique_ptr<std::thread> rpc_process_thread;
std::unique_ptr<nano::rpc> rpc;
std::unique_ptr<nano::rpc_handler_interface> rpc_handler;
if (config.rpc_enable)
{
rpc->start (config.rpc_enable);
if (config.rpc.rpc_in_process)
{
nano::rpc_config rpc_config;
auto error = nano::read_and_update_rpc_config (data_path, rpc_config);
if (error)
{
throw std::runtime_error ("Could not deserialize rpc_config file");
}
rpc_handler = std::make_unique<nano::inprocess_rpc_handler> (*node, config.rpc, [&ipc_server]() {
ipc_server.stop ();
});
rpc = nano::get_rpc (io_ctx, rpc_config, *rpc_handler);
rpc->start ();
}
else
{
#if BOOST_PROCESS_SUPPORTED
rpc_process = std::make_unique<boost::process::child> (config.rpc.rpc_path, "--daemon");
#else
auto rpc_exe_command = boost::str (boost::format ("%1% %2%") % config.rpc.rpc_path % "--daemon");
rpc_process_thread = std::make_unique<std::thread> ([ rpc_exe_command, &logger = node->logger ]() {
nano::thread_role::set (nano::thread_role::name::rpc_process_container);
std::system (rpc_exe_command.c_str ());
logger.always_log ("RPC server has stopped");
});
#endif
}
}
nano::ipc::ipc_server ipc (*node, *rpc);
runner = std::make_unique<nano::thread_runner> (io_ctx, node->config.io_threads);
runner->join ();
#if BOOST_PROCESS_SUPPORTED
if (rpc_process)
{
rpc_process->wait ();
}
#else
if (rpc_process_thread)
{
rpc_process_thread->join ();
}
#endif
}
else
{

View file

@ -2,10 +2,11 @@
#include <nano/lib/utility.hpp>
#include <nano/nano_node/daemon.hpp>
#include <nano/node/cli.hpp>
#include <nano/node/ipc.hpp>
#include <nano/node/json_handler.hpp>
#include <nano/node/node.hpp>
#include <nano/node/payment_observer_processor.hpp>
#include <nano/node/testing.hpp>
#include <nano/rpc/rpc.hpp>
#include <nano/rpc/rpc_handler.hpp>
#include <sstream>
#include <argon2.h>
@ -95,9 +96,9 @@ int main (int argc, char * const * argv)
("debug_profile_process", "Profile active blocks processing (only for nano_test_network)")
("debug_profile_votes", "Profile votes processing (only for nano_test_network)")
("debug_random_feed", "Generates output to RNG test suites")
("debug_rpc", "Read an RPC command from stdin and invoke it. Network operations will have no effect.")
("debug_validate_blocks", "Check all blocks for correct hash, signature, work value")
("debug_peers", "Display peer IPv6:port connections")
("debug_ipc", "Read an IPC command in JSON from stdin and invoke it. Network operations will have no effect")
("platform", boost::program_options::value<std::string> (), "Defines the <platform> for OpenCL commands")
("device", boost::program_options::value<std::string> (), "Defines <device> for OpenCL command")
("threads", boost::program_options::value<std::string> (), "Defines <threads> count for OpenCL command")
@ -725,18 +726,16 @@ int main (int argc, char * const * argv)
command_l << rpc_input_l;
}
auto response_handler_l ([](boost::property_tree::ptree const & tree_a) {
boost::property_tree::write_json (std::cout, tree_a);
auto response_handler_l ([](std::string const & response_a) {
std::cout << response_a;
// Terminate as soon as we have the result, even if background threads (like work generation) are running.
std::exit (0);
});
nano::inactive_node inactive_node_l (data_path);
nano::rpc_config rpc_config_l;
rpc_config_l.enable_control = true;
std::unique_ptr<nano::rpc> rpc_l = get_rpc (inactive_node_l.node->io_ctx, *inactive_node_l.node, rpc_config_l);
std::string req_id_l ("1");
nano::rpc_handler handler_l (*inactive_node_l.node, *rpc_l, command_l.str (), req_id_l, response_handler_l);
nano::node_rpc_config config;
nano::ipc::ipc_server server (*inactive_node_l.node, config);
nano::json_handler handler_l (*inactive_node_l.node, config, command_l.str (), response_handler_l);
handler_l.process_request ();
}
else if (vm.count ("debug_validate_blocks"))

View file

@ -0,0 +1,21 @@
add_executable (nano_rpc
entry.cpp)
target_link_libraries (nano_rpc
rpc
secure
Boost::filesystem
Boost::log
Boost::log_setup
Boost::program_options
Boost::system
Boost::thread
Boost::boost)
target_compile_definitions(nano_rpc
PUBLIC
-DACTIVE_NETWORK=${ACTIVE_NETWORK}
PRIVATE
-DNANO_VERSION_MAJOR=${CPACK_PACKAGE_VERSION_MAJOR}
-DNANO_VERSION_MINOR=${CPACK_PACKAGE_VERSION_MINOR}
-DNANO_VERSION_PATCH=${CPACK_PACKAGE_VERSION_PATCH})

139
nano/nano_rpc/entry.cpp Normal file
View file

@ -0,0 +1,139 @@
#include <boost/lexical_cast.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/program_options.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/utility.hpp>
#include <nano/nano_wallet/icon.hpp>
#include <nano/node/cli.hpp>
#include <nano/node/ipc.hpp>
#include <nano/node/working.hpp>
#include <nano/rpc/rpc.hpp>
#include <nano/rpc/rpc_request_processor.hpp>
namespace
{
void logging_init (boost::filesystem::path const & application_path_a)
{
static std::atomic_flag logging_already_added = ATOMIC_FLAG_INIT;
if (!logging_already_added.test_and_set ())
{
boost::log::add_common_attributes ();
auto path = application_path_a / "log";
uintmax_t max_size{ 128 * 1024 * 1024 };
uintmax_t rotation_size{ 4 * 1024 * 1024 };
bool flush{ true };
boost::log::add_file_log (boost::log::keywords::target = path, boost::log::keywords::file_name = path / "rpc_log_%Y-%m-%d_%H-%M-%S.%N.log", boost::log::keywords::rotation_size = rotation_size, boost::log::keywords::auto_flush = flush, boost::log::keywords::scan_method = boost::log::sinks::file::scan_method::scan_matching, boost::log::keywords::max_size = max_size, boost::log::keywords::format = "[%TimeStamp%]: %Message%");
}
}
void run (boost::filesystem::path const & data_path)
{
boost::filesystem::create_directories (data_path);
boost::system::error_code error_chmod;
nano::set_secure_perm_directory (data_path, error_chmod);
std::unique_ptr<nano::thread_runner> runner;
nano::rpc_config rpc_config;
auto error = nano::read_and_update_rpc_config (data_path, rpc_config);
if (!error)
{
logging_init (data_path);
boost::asio::io_context io_ctx;
try
{
nano::ipc_rpc_processor ipc_rpc_processor (io_ctx, rpc_config);
auto rpc = nano::get_rpc (io_ctx, rpc_config, ipc_rpc_processor);
rpc->start ();
runner = std::make_unique<nano::thread_runner> (io_ctx, rpc_config.io_threads);
runner->join ();
}
catch (const std::runtime_error & e)
{
std::cerr << "Error while running rpc (" << e.what () << ")\n";
}
}
else
{
std::cerr << "Error deserializing config: " << error.get_message () << std::endl;
}
}
}
int main (int argc, char * const * argv)
{
nano::set_umask ();
boost::program_options::options_description description ("Command line options");
// clang-format off
description.add_options ()
("help", "Print out options")
("version", "Prints out version")
("daemon", "Start RPC daemon")
("data_path", boost::program_options::value<std::string> (), "Use the supplied path as the data directory");
// clang-format on
boost::program_options::variables_map vm;
try
{
boost::program_options::store (boost::program_options::parse_command_line (argc, argv, description), vm);
}
catch (boost::program_options::error const & err)
{
std::cerr << err.what () << std::endl;
return 1;
}
boost::program_options::notify (vm);
int result (0);
auto network (vm.find ("network"));
if (network != vm.end ())
{
auto err (nano::network_constants::set_active_network (network->second.as<std::string> ()));
if (err)
{
std::cerr << err.get_message () << std::endl;
std::exit (1);
}
}
auto data_path_it = vm.find ("data_path");
if (data_path_it == vm.end ())
{
std::string error_string;
if (!nano::migrate_working_path (error_string))
{
std::cerr << error_string << std::endl;
return 1;
}
}
boost::filesystem::path data_path ((data_path_it != vm.end ()) ? data_path_it->second.as<std::string> () : nano::working_path ());
if (vm.count ("daemon") > 0)
{
run (data_path);
}
else if (vm.count ("version"))
{
if (NANO_VERSION_PATCH == 0)
{
std::cout << "Version " << NANO_MAJOR_MINOR_VERSION << std::endl;
}
else
{
std::cout << "Version " << NANO_MAJOR_MINOR_RC_VERSION << std::endl;
}
}
else
{
std::cout << description << std::endl;
result = -1;
}
return 1;
}

View file

@ -1,10 +1,13 @@
#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/rpcconfig.hpp>
#include <nano/lib/utility.hpp>
#include <nano/nano_wallet/icon.hpp>
#include <nano/node/cli.hpp>
#include <nano/node/ipc.hpp>
#include <nano/node/json_handler.hpp>
#include <nano/node/node_rpc_config.hpp>
#include <nano/node/working.hpp>
#include <nano/qt/qt.hpp>
#include <nano/rpc/rpc.hpp>
@ -14,13 +17,18 @@
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#ifndef BOOST_PROCESS_SUPPORTED
#error BOOST_PROCESS_SUPPORTED must be set, check configuration
#endif
#if BOOST_PROCESS_SUPPORTED
#include <boost/process.hpp>
#endif
class qt_wallet_config
{
public:
qt_wallet_config (boost::filesystem::path const & application_path_a) :
account (0),
rpc_enable (false),
opencl_enable (false)
qt_wallet_config (boost::filesystem::path const & data_path_a)
{
nano::random_pool::generate_block (wallet.bytes.data (), wallet.bytes.size ());
assert (!wallet.is_zero ());
@ -84,12 +92,13 @@ public:
json.put ("version", version_l.get ());
upgraded_a = true;
}
upgraded_a |= upgrade_json (version_l.get (), json);
auto wallet_l (json.get<std::string> ("wallet"));
auto account_l (json.get<std::string> ("account"));
auto node_l (json.get_required_child ("node"));
rpc_enable = json.get<bool> ("rpc_enable");
auto rpc_l (json.get_required_child ("rpc"));
rpc_enable = json.get<bool> ("rpc_enable");
opencl_enable = json.get<bool> ("opencl_enable");
auto opencl_l (json.get_required_child ("opencl"));
@ -107,7 +116,7 @@ public:
}
if (!rpc_l.get_error ())
{
rpc.deserialize_json (upgraded_a, rpc_l);
rpc.deserialize_json (upgraded_a, rpc_l, data_path);
}
if (!opencl_l.get_error ())
{
@ -139,10 +148,10 @@ public:
node.bootstrap_connections_max = 4;
node.serialize_json (node_l);
json.put_child ("node", node_l);
json.put ("rpc_enable", rpc_enable);
nano::jsonconfig rpc_l;
rpc.serialize_json (rpc_l);
json.put_child ("rpc", rpc_l);
json.put ("rpc_enable", rpc_enable);
json.put ("opencl_enable", opencl_enable);
nano::jsonconfig opencl_l;
opencl.serialize_json (opencl_l);
@ -168,12 +177,13 @@ public:
}
nano::uint256_union wallet;
nano::account account;
nano::account account{ 0 };
nano::node_config node;
bool rpc_enable;
nano::rpc_config rpc;
bool opencl_enable;
bool rpc_enable{ false };
nano::node_rpc_config rpc;
bool opencl_enable{ false };
nano::opencl_config opencl;
boost::filesystem::path data_path;
int json_version () const
{
return 4;
@ -205,6 +215,8 @@ bool update_config (qt_wallet_config & config_a, boost::filesystem::path const &
// Update json file with new account and/or wallet values
std::fstream config_file;
config_file.open (config_path_a.string (), std::ios_base::out | std::ios_base::trunc);
boost::system::error_code error_chmod;
nano::set_secure_perm_file (config_path_a, error_chmod);
error = config_a.serialize_json_stream (config_file);
}
}
@ -225,7 +237,7 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
splash->showMessage (QSplashScreen::tr ("Remember - Back Up Your Wallet Seed"), Qt::AlignBottom | Qt::AlignHCenter, Qt::darkGray);
application.processEvents ();
qt_wallet_config config (data_path);
auto config_path ((data_path / "config.json"));
auto config_path (nano::get_config_path (data_path));
int result (0);
nano::jsonconfig json;
auto error (json.read_and_update (config, config_path));
@ -279,17 +291,51 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
assert (wallet->exists (config.account));
update_config (config, config_path);
node->start ();
std::unique_ptr<nano::rpc> rpc = get_rpc (io_ctx, *node, config.rpc);
if (rpc)
nano::ipc::ipc_server ipc (*node, config.rpc);
#if BOOST_PROCESS_SUPPORTED
std::unique_ptr<boost::process::child> rpc_process;
#endif
std::unique_ptr<nano::rpc> rpc;
std::unique_ptr<nano::rpc_handler_interface> rpc_handler;
if (config.rpc_enable)
{
rpc->start (config.rpc_enable);
if (config.rpc.rpc_in_process)
{
nano::rpc_config rpc_config;
auto error = nano::read_and_update_rpc_config (data_path, rpc_config);
if (error)
{
throw std::runtime_error ("Could not deserialize rpc_config file");
}
rpc_handler = std::make_unique<nano::inprocess_rpc_handler> (*node, config.rpc);
rpc = nano::get_rpc (io_ctx, rpc_config, *rpc_handler);
rpc->start ();
}
else
{
#if BOOST_PROCESS_SUPPORTED
rpc_process = std::make_unique<boost::process::child> (config.rpc.rpc_path, "--daemon");
#else
show_error ("rpc_enable is set to true in the config. Set it to false and start the RPC server manually.");
#endif
}
}
nano::ipc::ipc_server ipc (*node, *rpc);
nano::thread_runner runner (io_ctx, node->config.io_threads);
QObject::connect (&application, &QApplication::aboutToQuit, [&]() {
ipc.stop ();
rpc->stop ();
node->stop ();
if (rpc)
{
rpc->stop ();
}
#if USE_BOOST_PROCESS
if (rpc_process)
{
rpc_process->terminate ();
}
#endif
});
application.postEvent (&processor, new nano_qt::eventloop_event ([&]() {
gui = std::make_shared<nano_qt::wallet> (application, processor, *node, wallet, config.account);

View file

@ -12,47 +12,58 @@ endif ()
add_library (node
${platform_sources}
${secure_rpc_sources}
blockprocessor.cpp
blockprocessor.hpp
bootstrap.cpp
blockprocessor.cpp
bootstrap.hpp
bootstrap.cpp
cli.hpp
cli.cpp
common.cpp
common.hpp
daemonconfig.hpp
common.cpp
daemonconfig.cpp
daemonconfig.hpp
ipc.hpp
ipc.cpp
ipcconfig.hpp
ipcconfig.cpp
lmdb.cpp
json_handler.hpp
json_handler.cpp
json_payment_observer.hpp
json_payment_observer.cpp
lmdb.hpp
logging.cpp
lmdb.cpp
logging.hpp
logging.cpp
nodeconfig.hpp
nodeconfig.cpp
node_observers.hpp
node_observers.cpp
node_rpc_config.hpp
node_rpc_config.cpp
node.hpp
node.cpp
openclwork.cpp
openclconfig.hpp
openclconfig.cpp
openclwork.hpp
openclwork.cpp
payment_observer_processor.hpp
payment_observer_processor.cpp
portmapping.hpp
portmapping.cpp
repcrawler.hpp
repcrawler.cpp
testing.hpp
testing.cpp
transport/tcp.cpp
transport/tcp.hpp
transport/transport.cpp
transport/tcp.cpp
transport/transport.hpp
transport/udp.cpp
transport/transport.cpp
transport/udp.hpp
signatures.cpp
transport/udp.cpp
signatures.hpp
stats.cpp
signatures.cpp
stats.hpp
stats.cpp
voting.hpp
voting.cpp
wallet.hpp
@ -65,13 +76,12 @@ add_library (node
xorshift.hpp)
target_link_libraries (node
rpc
secure
nano_lib
rpc
libminiupnpc-static
argon2
lmdb
${OPENSSL_LIBRARIES}
Boost::filesystem
Boost::log
Boost::log_setup

View file

@ -343,7 +343,7 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map
// Check/upgrade the config.json file.
{
nano::daemon_config config;
nano::daemon_config config (data_path);
auto error = nano::read_and_update_daemon_config (data_path, config);
if (error)
{

View file

@ -1,8 +1,8 @@
#include <nano/lib/config.hpp>
#include <nano/node/daemonconfig.hpp>
nano::daemon_config::daemon_config () :
rpc_enable (false),
opencl_enable (false)
nano::daemon_config::daemon_config (boost::filesystem::path const & data_path_a) :
data_path (data_path_a)
{
}
@ -35,12 +35,14 @@ nano::error nano::daemon_config::deserialize_json (bool & upgraded_a, nano::json
{
int version_l;
json.get_optional<int> ("version", version_l);
upgraded_a |= upgrade_json (version_l, json);
json.get_optional<bool> ("rpc_enable", rpc_enable);
auto rpc_l (json.get_required_child ("rpc"));
if (!rpc.deserialize_json (upgraded_a, rpc_l))
if (!rpc.deserialize_json (upgraded_a, rpc_l, data_path))
{
auto node_l (json.get_required_child ("node"));
if (!json.get_error ())
@ -48,6 +50,7 @@ nano::error nano::daemon_config::deserialize_json (bool & upgraded_a, nano::json
node.deserialize_json (upgraded_a, node_l);
}
}
if (!json.get_error ())
{
json.get_required<bool> ("opencl_enable", opencl_enable);

View file

@ -1,15 +1,16 @@
#pragma once
#include <nano/lib/errors.hpp>
#include <nano/node/node.hpp>
#include <nano/rpc/rpc.hpp>
#include <nano/node/node_rpc_config.hpp>
#include <nano/node/nodeconfig.hpp>
#include <nano/node/openclconfig.hpp>
namespace nano
{
class daemon_config
{
public:
daemon_config ();
daemon_config (boost::filesystem::path const & data_path);
nano::error deserialize_json (bool &, nano::jsonconfig &);
nano::error serialize_json (nano::jsonconfig &);
/**
@ -18,11 +19,12 @@ public:
* @param config Configuration to upgrade.
*/
bool upgrade_json (unsigned version, nano::jsonconfig & config);
bool rpc_enable;
nano::rpc_config rpc;
bool rpc_enable{ false };
nano::node_rpc_config rpc;
nano::node_config node;
bool opencl_enable;
bool opencl_enable{ false };
nano::opencl_config opencl;
boost::filesystem::path data_path;
int json_version () const
{
return 2;

View file

@ -16,9 +16,8 @@
#include <nano/lib/timer.hpp>
#include <nano/node/common.hpp>
#include <nano/node/ipc.hpp>
#include <nano/node/json_handler.hpp>
#include <nano/node/node.hpp>
#include <nano/rpc/rpc.hpp>
#include <nano/rpc/rpc_handler.hpp>
#include <thread>
using namespace boost::log;
@ -84,8 +83,8 @@ public:
});
}
/** Handler for payload_encoding::json_legacy and payload_encoding::json_unsafe */
void rpc_handle_query (bool allow_unsafe)
/** Handler for payload_encoding::json_legacy */
void handle_json_query (bool allow_unsafe)
{
session_timer.restart ();
auto request_id_l (std::to_string (server.id_dispenser.fetch_add (1)));
@ -93,18 +92,19 @@ public:
// This is called when nano::rpc_handler#process_request is done. We convert to
// json and write the response to the ipc socket with a length prefix.
auto this_l (this->shared_from_this ());
auto response_handler_l ([this_l, request_id_l](boost::property_tree::ptree const & tree_a) {
std::stringstream ostream;
boost::property_tree::write_json (ostream, tree_a);
ostream.flush ();
this_l->response_body = ostream.str ();
auto response_handler_l ([this_l, request_id_l](std::string const & body) {
this_l->response_body = body;
this_l->size_response = boost::endian::native_to_big (static_cast<uint32_t> (this_l->response_body.size ()));
std::vector<boost::asio::mutable_buffer> bufs = {
boost::asio::buffer (&this_l->size_response, sizeof (this_l->size_response)),
boost::asio::buffer (this_l->response_body)
};
if (this_l->node.config.logging.log_ipc ())
{
this_l->node.logger.always_log (boost::str (boost::format ("IPC/RPC request %1% completed in: %2% %3%") % request_id_l % this_l->session_timer.stop ().count () % this_l->session_timer.unit ()));
}
this_l->timer_start (std::chrono::seconds (this_l->config_transport.io_timeout));
boost::asio::async_write (this_l->socket, bufs, [this_l](boost::system::error_code const & error_a, size_t size_a) {
this_l->timer_cancel ();
@ -118,17 +118,16 @@ public:
}
});
if (this_l->node.config.logging.log_ipc ())
{
this_l->node.logger.always_log (boost::str (boost::format ("IPC/RPC request %1% completed in: %2% %3%") % request_id_l % this_l->session_timer.stop ().count () % this_l->session_timer.unit ()));
}
// Do not call any member variables here (like session_timer) as it's possible that the next request may already be underway.
});
node.stats.inc (nano::stat::type::ipc, nano::stat::detail::invocations);
auto body (std::string (reinterpret_cast<char *> (buffer.data ()), buffer.size ()));
// Note that if the rpc action is async, the shared_ptr<rpc_handler> lifetime will be extended by the action handler
auto handler (std::make_shared<nano::rpc_handler> (node, server.rpc, body, request_id_l, response_handler_l));
// Note that if the rpc action is async, the shared_ptr<json_handler> lifetime will be extended by the action handler
auto handler (std::make_shared<nano::json_handler> (node, server.node_rpc_config, body, response_handler_l, [& server = server]() {
server.stop ();
}));
// For unsafe actions to be allowed, the unsafe encoding must be used AND the transport config must allow it
handler->process_request (allow_unsafe && config_transport.allow_unsafe);
}
@ -157,7 +156,7 @@ public:
this_l->buffer.resize (this_l->buffer_size);
// Payload (ptree compliant JSON string)
this_l->async_read_exactly (this_l->buffer.data (), this_l->buffer_size, [this_l, allow_unsafe]() {
this_l->rpc_handle_query (allow_unsafe);
this_l->handle_json_query (allow_unsafe);
});
});
}
@ -291,8 +290,9 @@ private:
};
}
nano::ipc::ipc_server::ipc_server (nano::node & node_a, nano::rpc & rpc_a) :
node (node_a), rpc (rpc_a)
nano::ipc::ipc_server::ipc_server (nano::node & node_a, nano::node_rpc_config const & node_rpc_config_a) :
node (node_a),
node_rpc_config (node_rpc_config_a)
{
try
{

View file

@ -2,13 +2,12 @@
#include <atomic>
#include <nano/lib/ipc.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <vector>
#include <nano/lib/numbers.hpp>
#include <nano/node/node_rpc_config.hpp>
namespace nano
{
class node;
class rpc;
namespace ipc
{
@ -16,12 +15,13 @@ namespace ipc
class ipc_server
{
public:
ipc_server (nano::node & node, nano::rpc & rpc);
ipc_server (nano::node & node_a, nano::node_rpc_config const & node_rpc_config = nano::node_rpc_config{});
virtual ~ipc_server ();
void stop ();
nano::node & node;
nano::rpc & rpc;
nano::node_rpc_config const & node_rpc_config;
/** Unique counter/id shared across sessions */
std::atomic<uint64_t> id_dispenser{ 0 };

4562
nano/node/json_handler.cpp Normal file

File diff suppressed because it is too large Load diff

195
nano/node/json_handler.hpp Normal file
View file

@ -0,0 +1,195 @@
#pragma once
#include <boost/property_tree/ptree.hpp>
#include <functional>
#include <nano/lib/numbers.hpp>
#include <nano/node/wallet.hpp>
#include <nano/rpc/rpc.hpp>
#include <string>
namespace nano
{
class node;
class node_rpc_config;
class json_handler : public std::enable_shared_from_this<nano::json_handler>
{
public:
json_handler (nano::node &, nano::node_rpc_config const &, std::string const &, std::function<void(std::string const &)> const &, std::function<void()> stop_callback = []() {});
void process_request (bool unsafe = false);
void account_balance ();
void account_block_count ();
void account_count ();
void account_create ();
void account_get ();
void account_history ();
void account_info ();
void account_key ();
void account_list ();
void account_move ();
void account_remove ();
void account_representative ();
void account_representative_set ();
void account_weight ();
void accounts_balances ();
void accounts_create ();
void accounts_frontiers ();
void accounts_pending ();
void active_difficulty ();
void available_supply ();
void block_info ();
void block_confirm ();
void blocks ();
void blocks_info ();
void block_account ();
void block_count ();
void block_count_type ();
void block_create ();
void block_hash ();
void bootstrap ();
void bootstrap_any ();
void bootstrap_lazy ();
void bootstrap_status ();
void chain (bool = false);
void confirmation_active ();
void confirmation_history ();
void confirmation_info ();
void confirmation_quorum ();
void delegators ();
void delegators_count ();
void deterministic_key ();
void frontiers ();
void keepalive ();
void key_create ();
void key_expand ();
void ledger ();
void mnano_to_raw (nano::uint128_t = nano::Mxrb_ratio);
void mnano_from_raw (nano::uint128_t = nano::Mxrb_ratio);
void node_id ();
void node_id_delete ();
void password_change ();
void password_enter ();
void password_valid (bool = false);
void payment_begin ();
void payment_init ();
void payment_end ();
void payment_wait ();
void peers ();
void pending ();
void pending_exists ();
void process ();
void receive ();
void receive_minimum ();
void receive_minimum_set ();
void representatives ();
void representatives_online ();
void republish ();
void search_pending ();
void search_pending_all ();
void send ();
void sign ();
void stats ();
void stats_clear ();
void stop ();
void unchecked ();
void unchecked_clear ();
void unchecked_get ();
void unchecked_keys ();
void unopened ();
void uptime ();
void validate_account_number ();
void version ();
void wallet_add ();
void wallet_add_watch ();
void wallet_balances ();
void wallet_change_seed ();
void wallet_contains ();
void wallet_create ();
void wallet_destroy ();
void wallet_export ();
void wallet_frontiers ();
void wallet_history ();
void wallet_info ();
void wallet_key_valid ();
void wallet_ledger ();
void wallet_lock ();
void wallet_pending ();
void wallet_representative ();
void wallet_representative_set ();
void wallet_republish ();
void wallet_seed ();
void wallet_work_get ();
void work_cancel ();
void work_generate ();
void work_get ();
void work_peer_add ();
void work_peers ();
void work_peers_clear ();
void work_set ();
void work_validate ();
std::string body;
nano::node & node;
boost::property_tree::ptree request;
std::function<void(std::string const &)> response;
void response_errors ();
std::error_code ec;
std::string action;
boost::property_tree::ptree response_l;
std::shared_ptr<nano::wallet> wallet_impl ();
bool wallet_locked_impl (nano::transaction const &, std::shared_ptr<nano::wallet>);
bool wallet_account_impl (nano::transaction const &, std::shared_ptr<nano::wallet>, nano::account const &);
nano::account account_impl (std::string = "");
nano::amount amount_impl ();
std::shared_ptr<nano::block> block_impl (bool = true);
std::shared_ptr<nano::block> block_json_impl (bool = true);
nano::block_hash hash_impl (std::string = "hash");
nano::amount threshold_optional_impl ();
uint64_t work_optional_impl ();
uint64_t count_impl ();
uint64_t count_optional_impl (uint64_t = std::numeric_limits<uint64_t>::max ());
uint64_t offset_optional_impl (uint64_t = 0);
bool enable_sign_hash{ false };
std::function<void()> stop_callback;
nano::node_rpc_config const & node_rpc_config;
};
class inprocess_rpc_handler final : public nano::rpc_handler_interface
{
public:
inprocess_rpc_handler (nano::node & node_a, nano::node_rpc_config const & node_rpc_config_a, std::function<void()> stop_callback_a = []() {}) :
node (node_a),
stop_callback (stop_callback_a),
node_rpc_config (node_rpc_config_a)
{
}
void process_request (std::string const &, std::string const & body_a, std::function<void(std::string const &)> response_a) override
{
// Note that if the rpc action is async, the shared_ptr<json_handler> lifetime will be extended by the action handler
auto handler (std::make_shared<nano::json_handler> (node, node_rpc_config, body_a, response_a, [this]() {
this->stop_callback ();
this->stop ();
}));
handler->process_request ();
}
void stop () override
{
if (rpc)
{
rpc->stop ();
}
}
void rpc_instance (nano::rpc & rpc_a) override
{
rpc = rpc_a;
}
private:
nano::node & node;
boost::optional<nano::rpc &> rpc;
std::function<void()> stop_callback;
nano::node_rpc_config const & node_rpc_config;
};
}

View file

@ -0,0 +1,72 @@
#include <nano/lib/json_error_response.hpp>
#include <nano/node/ipc.hpp>
#include <nano/node/json_handler.hpp>
#include <nano/node/json_payment_observer.hpp>
#include <nano/node/node.hpp>
#include <nano/node/payment_observer_processor.hpp>
nano::json_payment_observer::json_payment_observer (nano::node & node_a, std::function<void(std::string const &)> const & response_a, nano::account const & account_a, nano::amount const & amount_a) :
node (node_a),
account (account_a),
amount (amount_a),
response (response_a)
{
completed.clear ();
}
void nano::json_payment_observer::start (uint64_t timeout)
{
auto this_l (shared_from_this ());
node.alarm.add (std::chrono::steady_clock::now () + std::chrono::milliseconds (timeout), [this_l]() {
this_l->complete (nano::payment_status::nothing);
});
}
void nano::json_payment_observer::observe ()
{
if (node.balance (account) >= amount.number ())
{
complete (nano::payment_status::success);
}
}
void nano::json_payment_observer::complete (nano::payment_status status)
{
auto already (completed.test_and_set ());
if (!already)
{
if (node.config.logging.log_ipc ())
{
node.logger.always_log (boost::str (boost::format ("Exiting json_payment_observer for account %1% status %2%") % account.to_account () % static_cast<unsigned> (status)));
}
switch (status)
{
case nano::payment_status::nothing:
{
boost::property_tree::ptree response_l;
response_l.put ("deprecated", "1");
response_l.put ("status", "nothing");
std::stringstream ostream;
boost::property_tree::write_json (ostream, response_l);
response (ostream.str ());
break;
}
case nano::payment_status::success:
{
boost::property_tree::ptree response_l;
response_l.put ("deprecated", "1");
response_l.put ("status", "success");
std::stringstream ostream;
boost::property_tree::write_json (ostream, response_l);
response (ostream.str ());
break;
}
default:
{
json_error_response (response, "Internal payment error");
break;
}
}
node.payment_observer_processor.erase (account);
}
}

View file

@ -0,0 +1,37 @@
#pragma once
#include <nano/node/node_observers.hpp>
#include <nano/node/wallet.hpp>
#include <string>
#include <vector>
namespace nano
{
class node;
enum class payment_status
{
not_a_status,
unknown,
nothing, // Timeout and nothing was received
//insufficient, // Timeout and not enough was received
//over, // More than requested received
//success_fork, // Amount received but it involved a fork
success // Amount received
};
class json_payment_observer final : public std::enable_shared_from_this<nano::json_payment_observer>
{
public:
json_payment_observer (nano::node &, std::function<void(std::string const &)> const &, nano::account const &, nano::amount const &);
void start (uint64_t);
void observe ();
void complete (nano::payment_status);
std::mutex mutex;
std::condition_variable condition;
nano::node & node;
nano::account account;
nano::amount amount;
std::function<void(std::string const &)> response;
std::atomic_flag completed;
};
}

View file

@ -45,7 +45,6 @@ nano::error nano::logging::serialize_json (nano::jsonconfig & json) const
json.put ("network_node_id_handshake", network_node_id_handshake_logging_value);
json.put ("node_lifetime_tracing", node_lifetime_tracing_value);
json.put ("insufficient_work", insufficient_work_logging_value);
json.put ("log_rpc", log_rpc_value);
json.put ("log_ipc", log_ipc_value);
json.put ("bulk_pull", bulk_pull_logging_value);
json.put ("work_generation_time", work_generation_time_value);
@ -88,6 +87,7 @@ bool nano::logging::upgrade_json (unsigned version_a, nano::jsonconfig & json)
upgraded_l = true;
case 6:
json.put ("min_time_between_output", min_time_between_log_output.count ());
json.erase ("log_rpc");
upgraded_l = true;
break;
case 7:
@ -132,7 +132,6 @@ nano::error nano::logging::deserialize_json (bool & upgraded_a, nano::jsonconfig
json.get<bool> ("network_node_id_handshake", network_node_id_handshake_logging_value);
json.get<bool> ("node_lifetime_tracing", node_lifetime_tracing_value);
json.get<bool> ("insufficient_work", insufficient_work_logging_value);
json.get<bool> ("log_rpc", log_rpc_value);
json.get<bool> ("log_ipc", log_ipc_value);
json.get<bool> ("bulk_pull", bulk_pull_logging_value);
json.get<bool> ("work_generation_time", work_generation_time_value);
@ -203,11 +202,6 @@ bool nano::logging::insufficient_work_logging () const
return network_logging () && insufficient_work_logging_value;
}
bool nano::logging::log_rpc () const
{
return network_logging () && log_rpc_value;
}
bool nano::logging::log_ipc () const
{
return network_logging () && log_ipc_value;

View file

@ -34,7 +34,6 @@ public:
bool insufficient_work_logging () const;
bool upnp_details_logging () const;
bool timing_logging () const;
bool log_rpc () const;
bool log_ipc () const;
bool bulk_pull_logging () const;
bool callback_logging () const;
@ -53,7 +52,6 @@ public:
bool network_node_id_handshake_logging_value{ false };
bool node_lifetime_tracing_value{ false };
bool insufficient_work_logging_value{ true };
bool log_rpc_value{ true };
bool log_ipc_value{ true };
bool bulk_pull_logging_value{ false };
bool work_generation_time_value{ true };

View file

@ -950,18 +950,6 @@ void nano::vote_processor::calculate_weights ()
namespace nano
{
std::unique_ptr<seq_con_info_component> collect_seq_con_info (node_observers & node_observers, const std::string & name)
{
auto composite = std::make_unique<seq_con_info_composite> (name);
composite->add_component (collect_seq_con_info (node_observers.blocks, "blocks"));
composite->add_component (collect_seq_con_info (node_observers.wallet, "wallet"));
composite->add_component (collect_seq_con_info (node_observers.vote, "vote"));
composite->add_component (collect_seq_con_info (node_observers.account_balance, "account_balance"));
composite->add_component (collect_seq_con_info (node_observers.endpoint, "endpoint"));
composite->add_component (collect_seq_con_info (node_observers.disconnect, "disconnect"));
return composite;
}
std::unique_ptr<seq_con_info_component> collect_seq_con_info (vote_processor & vote_processor, const std::string & name)
{
size_t votes_count = 0;
@ -1064,6 +1052,7 @@ online_reps (*this, config.online_weight_minimum.number ()),
stats (config.stat_config),
vote_uniquer (block_uniquer),
active (*this, delay_frontier_confirmation_height_updating),
payment_observer_processor (observers.blocks),
startup_time (std::chrono::steady_clock::now ())
{
if (config.websocket_config.enabled)

View file

@ -4,7 +4,9 @@
#include <nano/node/blockprocessor.hpp>
#include <nano/node/bootstrap.hpp>
#include <nano/node/logging.hpp>
#include <nano/node/node_observers.hpp>
#include <nano/node/nodeconfig.hpp>
#include <nano/node/payment_observer_processor.hpp>
#include <nano/node/portmapping.hpp>
#include <nano/node/repcrawler.hpp>
#include <nano/node/signatures.hpp>
@ -371,18 +373,6 @@ public:
bool wallets_store_init{ false };
bool wallet_init{ false };
};
class node_observers final
{
public:
nano::observer_set<std::shared_ptr<nano::block>, nano::account const &, nano::uint128_t const &, bool> blocks;
nano::observer_set<bool> wallet;
nano::observer_set<nano::transaction const &, std::shared_ptr<nano::vote>, std::shared_ptr<nano::transport::channel>> vote;
nano::observer_set<nano::account const &, bool> account_balance;
nano::observer_set<std::shared_ptr<nano::transport::channel>> endpoint;
nano::observer_set<> disconnect;
};
std::unique_ptr<seq_con_info_component> collect_seq_con_info (node_observers & node_observers, const std::string & name);
class vote_processor final
{
@ -506,6 +496,7 @@ public:
nano::block_uniquer block_uniquer;
nano::vote_uniquer vote_uniquer;
nano::active_transactions active;
nano::payment_observer_processor payment_observer_processor;
const std::chrono::steady_clock::time_point startup_time;
std::chrono::seconds unchecked_cutoff = std::chrono::seconds (7 * 24 * 60 * 60); // Week
static double constexpr price_max = 16.0;

View file

@ -0,0 +1,13 @@
#include <nano/node/node_observers.hpp>
std::unique_ptr<nano::seq_con_info_component> nano::collect_seq_con_info (nano::node_observers & node_observers, const std::string & name)
{
auto composite = std::make_unique<nano::seq_con_info_composite> (name);
composite->add_component (collect_seq_con_info (node_observers.blocks, "blocks"));
composite->add_component (collect_seq_con_info (node_observers.wallet, "wallet"));
composite->add_component (collect_seq_con_info (node_observers.vote, "vote"));
composite->add_component (collect_seq_con_info (node_observers.account_balance, "account_balance"));
composite->add_component (collect_seq_con_info (node_observers.endpoint, "endpoint"));
composite->add_component (collect_seq_con_info (node_observers.disconnect, "disconnect"));
return composite;
}

View file

@ -0,0 +1,24 @@
#pragma once
#include <nano/lib/blocks.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/utility.hpp>
#include <nano/node/transport/transport.hpp>
#include <nano/secure/blockstore.hpp>
namespace nano
{
class node_observers final
{
public:
using blocks_t = nano::observer_set<std::shared_ptr<nano::block>, nano::account const &, nano::uint128_t const &, bool>;
blocks_t blocks;
nano::observer_set<bool> wallet;
nano::observer_set<nano::transaction const &, std::shared_ptr<nano::vote>, std::shared_ptr<nano::transport::channel>> vote;
nano::observer_set<nano::account const &, bool> account_balance;
nano::observer_set<std::shared_ptr<nano::transport::channel>> endpoint;
nano::observer_set<> disconnect;
};
std::unique_ptr<seq_con_info_component> collect_seq_con_info (node_observers & node_observers, const std::string & name);
}

View file

@ -0,0 +1,78 @@
#include <nano/node/node_rpc_config.hpp>
#include <nano/lib/blocks.hpp>
#include <nano/lib/config.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/rpcconfig.hpp>
nano::error nano::node_rpc_config::serialize_json (nano::jsonconfig & json) const
{
json.put ("version", json_version ());
json.put ("enable_sign_hash", enable_sign_hash);
json.put ("max_work_generate_difficulty", nano::to_string_hex (max_work_generate_difficulty));
json.put ("rpc_path", rpc_path);
json.put ("rpc_in_process", rpc_in_process);
return json.get_error ();
}
nano::error nano::node_rpc_config::deserialize_json (bool & upgraded_a, nano::jsonconfig & json, boost::filesystem::path const & data_path)
{
auto version_l (json.get_optional<unsigned> ("version"));
if (!version_l)
{
json.erase ("frontier_request_limit");
json.erase ("chain_request_limit");
// Don't migrate enable_sign_hash as this is not needed by the external rpc process, but first save it.
json.get_optional ("enable_sign_hash", enable_sign_hash, false);
json.erase ("enable_sign_hash");
json.erase ("max_work_generate_difficulty");
migrate (json, data_path);
json.put ("enable_sign_hash", enable_sign_hash);
json.put ("max_work_generate_difficulty", nano::to_string_hex (max_work_generate_difficulty));
// Remove options no longer needed after migration
json.erase ("enable_control");
json.erase ("address");
json.erase ("port");
json.erase ("max_json_depth");
json.erase ("max_request_size");
version_l = 1;
json.put ("version", *version_l);
json.put ("rpc_path", get_default_rpc_filepath ());
auto rpc_in_process_l = json.get_optional<bool> ("rpc_in_process");
if (!rpc_in_process_l)
{
json.put ("rpc_in_process", true);
}
upgraded_a = true;
}
json.get_optional<bool> ("enable_sign_hash", enable_sign_hash);
std::string max_work_generate_difficulty_text;
json.get_optional<std::string> ("max_work_generate_difficulty", max_work_generate_difficulty_text);
if (!max_work_generate_difficulty_text.empty ())
{
nano::from_string_hex (max_work_generate_difficulty_text, max_work_generate_difficulty);
}
json.get_optional<std::string> ("rpc_path", rpc_path);
json.get_optional<bool> ("rpc_in_process", rpc_in_process);
return json.get_error ();
}
void nano::node_rpc_config::migrate (nano::jsonconfig & json, boost::filesystem::path const & data_path)
{
nano::jsonconfig rpc_json;
auto rpc_config_path = nano::get_rpc_config_path (data_path);
auto rpc_error (rpc_json.read<nano::rpc_config> (rpc_config_path));
if (rpc_error || rpc_json.empty ())
{
// Migrate RPC info across
json.write (rpc_config_path);
}
}

View file

@ -0,0 +1,26 @@
#pragma once
#include <boost/filesystem.hpp>
#include <nano/lib/rpcconfig.hpp>
#include <string>
namespace nano
{
class node_rpc_config final
{
public:
nano::error serialize_json (nano::jsonconfig &) const;
nano::error deserialize_json (bool & upgraded_a, nano::jsonconfig &, boost::filesystem::path const & data_path);
bool enable_sign_hash{ false };
uint64_t max_work_generate_difficulty{ 0xffffffffc0000000 };
std::string rpc_path{ get_default_rpc_filepath () };
bool rpc_in_process{ true };
static int json_version ()
{
return 1;
}
private:
void migrate (nano::jsonconfig & json, boost::filesystem::path const & data_path);
};
}

View file

@ -1,5 +1,7 @@
#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/config.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/rpcconfig.hpp>
#include <nano/node/nodeconfig.hpp>
// NOTE: to reduce compile times, this include can be replaced by more narrow includes
// once nano::network is factored out of node.{c|h}pp

View file

@ -0,0 +1,24 @@
#include <nano/node/openclconfig.hpp>
nano::opencl_config::opencl_config (unsigned platform_a, unsigned device_a, unsigned threads_a) :
platform (platform_a),
device (device_a),
threads (threads_a)
{
}
nano::error nano::opencl_config::serialize_json (nano::jsonconfig & json) const
{
json.put ("platform", platform);
json.put ("device", device);
json.put ("threads", threads);
return json.get_error ();
}
nano::error nano::opencl_config::deserialize_json (nano::jsonconfig & json)
{
json.get_optional<unsigned> ("platform", platform);
json.get_optional<unsigned> ("device", device);
json.get_optional<unsigned> ("threads", threads);
return json.get_error ();
}

View file

@ -0,0 +1,19 @@
#pragma once
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
namespace nano
{
class opencl_config
{
public:
opencl_config () = default;
opencl_config (unsigned, unsigned, unsigned);
nano::error serialize_json (nano::jsonconfig &) const;
nano::error deserialize_json (nano::jsonconfig &);
unsigned platform{ 0 };
unsigned device{ 0 };
unsigned threads{ 1024 * 1024 };
};
}

View file

@ -1,8 +1,8 @@
#include <nano/node/openclwork.hpp>
#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/utility.hpp>
#include <nano/node/node.hpp>
#include <nano/node/openclconfig.hpp>
#include <nano/node/openclwork.hpp>
#include <nano/node/wallet.hpp>
#include <array>
@ -484,36 +484,6 @@ void nano::opencl_environment::dump (std::ostream & stream)
}
}
nano::opencl_config::opencl_config () :
platform (0),
device (0),
threads (1024 * 1024)
{
}
nano::opencl_config::opencl_config (unsigned platform_a, unsigned device_a, unsigned threads_a) :
platform (platform_a),
device (device_a),
threads (threads_a)
{
}
nano::error nano::opencl_config::serialize_json (nano::jsonconfig & json) const
{
json.put ("platform", platform);
json.put ("device", device);
json.put ("threads", threads);
return json.get_error ();
}
nano::error nano::opencl_config::deserialize_json (nano::jsonconfig & json)
{
json.get_optional<unsigned> ("platform", platform);
json.get_optional<unsigned> ("device", device);
json.get_optional<unsigned> ("threads", threads);
return json.get_error ();
}
nano::opencl_work::opencl_work (bool & error_a, nano::opencl_config const & config_a, nano::opencl_environment & environment_a, nano::logging & logging_a) :
config (config_a),
context (0),

View file

@ -4,6 +4,7 @@
#include <boost/property_tree/ptree.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/node/openclconfig.hpp>
#include <nano/node/xorshift.hpp>
#include <map>
@ -36,17 +37,6 @@ public:
};
union uint256_union;
class work_pool;
class opencl_config
{
public:
opencl_config ();
opencl_config (unsigned, unsigned, unsigned);
nano::error serialize_json (nano::jsonconfig &) const;
nano::error deserialize_json (nano::jsonconfig &);
unsigned platform;
unsigned device;
unsigned threads;
};
class opencl_work
{
public:

View file

@ -0,0 +1,41 @@
#include <nano/node/payment_observer_processor.hpp>
#include <nano/node/json_payment_observer.hpp>
nano::payment_observer_processor::payment_observer_processor (nano::node_observers::blocks_t & blocks)
{
blocks.add ([this](std::shared_ptr<nano::block> block_a, nano::account const & account_a, nano::uint128_t const &, bool) {
observer_action (account_a);
});
}
void nano::payment_observer_processor::observer_action (nano::account const & account_a)
{
std::shared_ptr<nano::json_payment_observer> observer;
{
std::lock_guard<std::mutex> lock (mutex);
auto existing (payment_observers.find (account_a));
if (existing != payment_observers.end ())
{
observer = existing->second;
}
}
if (observer != nullptr)
{
observer->observe ();
}
}
void nano::payment_observer_processor::add (nano::account const & account_a, std::shared_ptr<nano::json_payment_observer> payment_observer_a)
{
std::lock_guard<std::mutex> lock (mutex);
assert (payment_observers.find (account_a) == payment_observers.end ());
payment_observers[account_a] = payment_observer_a;
}
void nano::payment_observer_processor::erase (nano::account & account_a)
{
std::lock_guard<std::mutex> lock (mutex);
assert (payment_observers.find (account_a) != payment_observers.end ());
payment_observers.erase (account_a);
}

View file

@ -0,0 +1,21 @@
#pragma once
#include <nano/node/node_observers.hpp>
namespace nano
{
class json_payment_observer;
class payment_observer_processor final
{
public:
explicit payment_observer_processor (nano::node_observers::blocks_t & blocks);
void observer_action (nano::account const & account_a);
void add (nano::account const & account_a, std::shared_ptr<nano::json_payment_observer> payment_observer_a);
void erase (nano::account & account_a);
private:
std::mutex mutex;
std::unordered_map<nano::account, std::shared_ptr<nano::json_payment_observer>> payment_observers;
};
}

View file

@ -1,7 +1,7 @@
#pragma once
#include <boost/asio/ip/address_v4.hpp>
#include <miniupnpc.h>
#include <miniupnp/miniupnpc/miniupnpc.h>
#include <mutex>
#include <nano/lib/config.hpp>

View file

@ -532,3 +532,19 @@ void nano::landing::distribute_ongoing ()
std::chrono::seconds constexpr nano::landing::distribution_interval;
std::chrono::seconds constexpr nano::landing::sleep_seconds;
namespace nano
{
void cleanup_test_directories_on_exit ()
{
// Makes sure everything is cleaned up
nano::logging::release_file_sink ();
// Clean up tmp directories created by the tests. Since it's sometimes useful to
// see log files after test failures, an environment variable is supported to
// retain the files.
if (std::getenv ("TEST_KEEP_TMPDIRS") == nullptr)
{
nano::remove_temporary_directories ();
}
}
}

View file

@ -2,6 +2,7 @@
#include <boost/asio/buffer.hpp>
#include <nano/node/common.hpp>
#include <nano/node/stats.hpp>
namespace nano

View file

@ -1,9 +1,9 @@
#include <QApplication>
#include <gtest/gtest.h>
#include <nano/secure/utility.hpp>
QApplication * test_application = nullptr;
namespace nano
{
void cleanup_test_directories_on_exit ();
void force_nano_test_network ();
}
@ -14,6 +14,6 @@ int main (int argc, char ** argv)
test_application = &application;
testing::InitGoogleTest (&argc, argv);
auto res = RUN_ALL_TESTS ();
nano::cleanp_test_directories_on_exit ();
nano::cleanup_test_directories_on_exit ();
return res;
}

View file

@ -9,16 +9,11 @@ add_library (rpc
rpc_connection.hpp
rpc_connection.cpp
rpc_handler.hpp
rpc_handler.cpp)
rpc_handler.cpp
rpc_request_processor.hpp
rpc_request_processor.cpp)
target_link_libraries (rpc
node
nano_lib
${OPENSSL_LIBRARIES}
Boost::boost)
target_compile_definitions(rpc
PRIVATE
-DNANO_VERSION_MAJOR=${CPACK_PACKAGE_VERSION_MAJOR}
-DNANO_VERSION_MINOR=${CPACK_PACKAGE_VERSION_MINOR}
-DNANO_VERSION_PATCH=${CPACK_PACKAGE_VERSION_PATCH})

View file

@ -1,62 +1,50 @@
#include <boost/algorithm/string.hpp>
#include <nano/lib/config.hpp>
#include <nano/lib/interface.h>
#include <nano/node/node.hpp>
#include <boost/format.hpp>
#include <nano/lib/rpc_handler_interface.hpp>
#include <nano/rpc/rpc.hpp>
#include <nano/rpc/rpc_connection.hpp>
#include <nano/rpc/rpc_handler.hpp>
#ifdef NANO_SECURE_RPC
#include <nano/rpc/rpc_secure.hpp>
#endif
#include <nano/lib/errors.hpp>
nano::rpc::rpc (boost::asio::io_context & io_ctx_a, nano::node & node_a, nano::rpc_config const & config_a) :
acceptor (io_ctx_a),
nano::rpc::rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a) :
config (config_a),
node (node_a)
acceptor (io_ctx_a),
logger (std::chrono::milliseconds (0)),
io_ctx (io_ctx_a),
rpc_handler_interface (rpc_handler_interface_a)
{
rpc_handler_interface.rpc_instance (*this);
}
void nano::rpc::add_block_observer ()
nano::rpc::~rpc ()
{
node.observers.blocks.add ([this](std::shared_ptr<nano::block> block_a, nano::account const & account_a, nano::uint128_t const &, bool) {
observer_action (account_a);
});
if (!stopped)
{
stop ();
}
}
void nano::rpc::start (bool rpc_enabled_a)
void nano::rpc::start ()
{
if (rpc_enabled_a)
auto endpoint (boost::asio::ip::tcp::endpoint (config.address, config.port));
acceptor.open (endpoint.protocol ());
acceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true));
boost::system::error_code ec;
acceptor.bind (endpoint, ec);
if (ec)
{
auto endpoint (nano::tcp_endpoint (config.address, config.port));
acceptor.open (endpoint.protocol ());
acceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true));
boost::system::error_code ec;
acceptor.bind (endpoint, ec);
if (ec)
{
node.logger.always_log (boost::str (boost::format ("Error while binding for RPC on port %1%: %2%") % endpoint.port () % ec.message ()));
throw std::runtime_error (ec.message ());
}
acceptor.listen ();
}
add_block_observer ();
if (rpc_enabled_a)
{
accept ();
logger.always_log (boost::str (boost::format ("Error while binding for RPC on port %1%: %2%") % endpoint.port () % ec.message ()));
throw std::runtime_error (ec.message ());
}
acceptor.listen ();
accept ();
}
void nano::rpc::accept ()
{
auto connection (std::make_shared<nano::rpc_connection> (node, *this));
auto connection (std::make_shared<nano::rpc_connection> (config, network_constants, io_ctx, logger, rpc_handler_interface));
acceptor.async_accept (connection->socket, [this, connection](boost::system::error_code const & ec) {
if (ec != boost::asio::error::operation_aborted && acceptor.is_open ())
{
@ -68,116 +56,32 @@ void nano::rpc::accept ()
}
else
{
this->node.logger.always_log (boost::str (boost::format ("Error accepting RPC connections: %1% (%2%)") % ec.message () % ec.value ()));
logger.always_log (boost::str (boost::format ("Error accepting RPC connections: %1% (%2%)") % ec.message () % ec.value ()));
}
});
}
void nano::rpc::stop ()
{
stopped = true;
acceptor.close ();
}
void nano::rpc::observer_action (nano::account const & account_a)
{
std::shared_ptr<nano::payment_observer> observer;
{
std::lock_guard<std::mutex> lock (mutex);
auto existing (payment_observers.find (account_a));
if (existing != payment_observers.end ())
{
observer = existing->second;
}
}
if (observer != nullptr)
{
observer->observe ();
}
}
nano::payment_observer::payment_observer (std::function<void(boost::property_tree::ptree const &)> const & response_a, nano::rpc & rpc_a, nano::account const & account_a, nano::amount const & amount_a) :
rpc (rpc_a),
account (account_a),
amount (amount_a),
response (response_a)
{
completed.clear ();
}
void nano::payment_observer::start (uint64_t timeout)
{
auto this_l (shared_from_this ());
rpc.node.alarm.add (std::chrono::steady_clock::now () + std::chrono::milliseconds (timeout), [this_l]() {
this_l->complete (nano::payment_status::nothing);
});
}
nano::payment_observer::~payment_observer ()
{
}
void nano::payment_observer::observe ()
{
if (rpc.node.balance (account) >= amount.number ())
{
complete (nano::payment_status::success);
}
}
void nano::payment_observer::complete (nano::payment_status status)
{
auto already (completed.test_and_set ());
if (!already)
{
if (rpc.node.config.logging.log_rpc ())
{
rpc.node.logger.always_log (boost::str (boost::format ("Exiting payment_observer for account %1% status %2%") % account.to_account () % static_cast<unsigned> (status)));
}
switch (status)
{
case nano::payment_status::nothing:
{
boost::property_tree::ptree response_l;
response_l.put ("deprecated", "1");
response_l.put ("status", "nothing");
response (response_l);
break;
}
case nano::payment_status::success:
{
boost::property_tree::ptree response_l;
response_l.put ("deprecated", "1");
response_l.put ("status", "success");
response (response_l);
break;
}
default:
{
error_response (response, "Internal payment error");
break;
}
}
std::lock_guard<std::mutex> lock (rpc.mutex);
assert (rpc.payment_observers.find (account) != rpc.payment_observers.end ());
rpc.payment_observers.erase (account);
}
}
std::unique_ptr<nano::rpc> nano::get_rpc (boost::asio::io_context & io_ctx_a, nano::node & node_a, nano::rpc_config const & config_a)
std::unique_ptr<nano::rpc> nano::get_rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a)
{
std::unique_ptr<rpc> impl;
if (config_a.secure.enable)
{
#ifdef NANO_SECURE_RPC
impl.reset (new rpc_secure (io_ctx_a, node_a, config_a));
impl = std::make_unique<rpc_secure> (io_ctx_a, config_a, rpc_handler_interface_a);
#else
std::cerr << "RPC configured for TLS, but the node is not compiled with TLS support" << std::endl;
#endif
}
else
{
impl.reset (new rpc (io_ctx_a, node_a, config_a));
impl = std::make_unique<rpc> (io_ctx_a, config_a, rpc_handler_interface_a);
}
return impl;

View file

@ -1,74 +1,32 @@
#pragma once
#include <atomic>
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <nano/lib/blocks.hpp>
#include <nano/lib/config.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/logger_mt.hpp>
#include <nano/lib/rpc_handler_interface.hpp>
#include <nano/lib/rpcconfig.hpp>
#include <nano/secure/blockstore.hpp>
#include <nano/secure/utility.hpp>
#include <unordered_map>
namespace nano
{
class node;
enum class payment_status
{
not_a_status,
unknown,
nothing, // Timeout and nothing was received
//insufficient, // Timeout and not enough was received
//over, // More than requested received
//success_fork, // Amount received but it involved a fork
success // Amount received
};
class wallet;
class payment_observer;
class rpc_handler_interface;
class rpc
{
public:
rpc (boost::asio::io_context &, nano::node &, nano::rpc_config const &);
virtual ~rpc () = default;
/**
* Start serving RPC requests if \p rpc_enabled_a, otherwise this will only
* add a block observer since requests may still arrive via IPC.
*/
void start (bool rpc_enabled_a = true);
void add_block_observer ();
rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a);
virtual ~rpc ();
void start ();
virtual void accept ();
void stop ();
void observer_action (nano::account const &);
boost::asio::ip::tcp::acceptor acceptor;
std::mutex mutex;
std::unordered_map<nano::account, std::shared_ptr<nano::payment_observer>> payment_observers;
nano::rpc_config config;
nano::node & node;
bool on;
};
class payment_observer : public std::enable_shared_from_this<nano::payment_observer>
{
public:
payment_observer (std::function<void(boost::property_tree::ptree const &)> const &, nano::rpc &, nano::account const &, nano::amount const &);
~payment_observer ();
void start (uint64_t);
void observe ();
void complete (nano::payment_status);
std::mutex mutex;
std::condition_variable condition;
nano::rpc & rpc;
nano::account account;
nano::amount amount;
std::function<void(boost::property_tree::ptree const &)> response;
std::atomic_flag completed;
nano::rpc_config config;
boost::asio::ip::tcp::acceptor acceptor;
nano::logger_mt logger;
boost::asio::io_context & io_ctx;
nano::network_constants network_constants;
nano::rpc_handler_interface & rpc_handler_interface;
bool stopped{ false };
};
/** Returns the correct RPC implementation based on TLS configuration */
std::unique_ptr<nano::rpc> get_rpc (boost::asio::io_context & io_ctx_a, nano::node & node_a, nano::rpc_config const & config_a);
std::unique_ptr<nano::rpc> get_rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a);
}

View file

@ -1,14 +1,20 @@
#include <boost/algorithm/string/predicate.hpp>
#include <boost/format.hpp>
#include <nano/lib/config.hpp>
#include <nano/rpc/rpc.hpp>
#include <nano/lib/json_error_response.hpp>
#include <nano/lib/logger_mt.hpp>
#include <nano/lib/rpc_handler_interface.hpp>
#include <nano/lib/rpcconfig.hpp>
#include <nano/rpc/rpc_connection.hpp>
#include <nano/rpc/rpc_handler.hpp>
nano::rpc_connection::rpc_connection (nano::node & node_a, nano::rpc & rpc_a) :
node (node_a.shared ()),
rpc (rpc_a),
socket (node_a.io_ctx)
nano::rpc_connection::rpc_connection (nano::rpc_config const & rpc_config, nano::network_constants const & network_constants, boost::asio::io_context & io_ctx, nano::logger_mt & logger, nano::rpc_handler_interface & rpc_handler_interface) :
socket (io_ctx),
io_ctx (io_ctx),
logger (logger),
rpc_config (rpc_config),
network_constants (network_constants),
rpc_handler_interface (rpc_handler_interface)
{
responded.clear ();
}
@ -52,8 +58,8 @@ void nano::rpc_connection::read ()
auto header_parser (std::make_shared<boost::beast::http::request_parser<boost::beast::http::empty_body>> ());
std::promise<size_t> header_available_promise;
std::future<size_t> header_available = header_available_promise.get_future ();
header_parser->body_limit (rpc.config.max_request_size);
if (!node->network_params.network.is_test_network ())
header_parser->body_limit (rpc_config.max_request_size);
if (!network_constants.is_test_network ())
{
boost::beast::http::async_read_header (socket, buffer, *header_parser, [this_l, header_parser, &header_available_promise, &header_error](boost::system::error_code const & ec, size_t bytes_transferred) {
size_t header_response_bytes_written = 0;
@ -72,13 +78,13 @@ void nano::rpc_connection::read ()
else
{
header_error = ec;
this_l->node->logger.always_log ("RPC header error: ", ec.message ());
this_l->logger.always_log ("RPC header error: ", ec.message ());
}
header_available_promise.set_value (header_response_bytes_written);
});
// Avait header
// Await header
header_available.get ();
}
@ -88,32 +94,27 @@ void nano::rpc_connection::read ()
boost::beast::http::async_read (socket, buffer, *body_parser, [this_l, body_parser](boost::system::error_code const & ec, size_t bytes_transferred) {
if (!ec)
{
this_l->node->background ([this_l, body_parser]() {
// equivalent to background
this_l->io_ctx.post ([this_l, body_parser]() {
auto & req (body_parser->get ());
auto start (std::chrono::steady_clock::now ());
auto version (req.version ());
std::string request_id (boost::str (boost::format ("%1%") % boost::io::group (std::hex, std::showbase, reinterpret_cast<uintptr_t> (this_l.get ()))));
auto response_handler ([this_l, version, start, request_id](boost::property_tree::ptree const & tree_a) {
std::stringstream ostream;
boost::property_tree::write_json (ostream, tree_a);
ostream.flush ();
auto body (ostream.str ());
auto response_handler ([this_l, version, start, request_id](std::string const & tree_a) {
auto body = tree_a;
this_l->write_result (body, version);
boost::beast::http::async_write (this_l->socket, this_l->res, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) {
this_l->write_completion_handler (this_l);
});
if (this_l->node->config.logging.log_rpc ())
{
this_l->node->logger.always_log (boost::str (boost::format ("RPC request %2% completed in: %1% microseconds") % std::chrono::duration_cast<std::chrono::microseconds> (std::chrono::steady_clock::now () - start).count () % request_id));
}
this_l->logger.always_log (boost::str (boost::format ("RPC request %2% completed in: %1% microseconds") % std::chrono::duration_cast<std::chrono::microseconds> (std::chrono::steady_clock::now () - start).count () % request_id));
});
auto method = req.method ();
switch (method)
{
case boost::beast::http::verb::post:
{
auto handler (std::make_shared<nano::rpc_handler> (*this_l->node, this_l->rpc, req.body (), request_id, response_handler));
auto handler (std::make_shared<nano::rpc_handler> (this_l->rpc_config, req.body (), request_id, response_handler, this_l->rpc_handler_interface, this_l->logger));
handler->process_request ();
break;
}
@ -128,7 +129,7 @@ void nano::rpc_connection::read ()
}
default:
{
error_response (response_handler, "Can only POST requests");
json_error_response (response_handler, "Can only POST requests");
break;
}
}
@ -136,28 +137,24 @@ void nano::rpc_connection::read ()
}
else
{
this_l->node->logger.always_log ("RPC read error: ", ec.message ());
this_l->logger.always_log ("RPC read error: ", ec.message ());
}
});
}
else
{
// Respond with the reason for the invalid header
auto response_handler ([this_l](boost::property_tree::ptree const & tree_a) {
std::stringstream ostream;
boost::property_tree::write_json (ostream, tree_a);
ostream.flush ();
auto body (ostream.str ());
this_l->write_result (body, 11);
auto response_handler ([this_l](std::string const & tree_a) {
this_l->write_result (tree_a, 11);
boost::beast::http::async_write (this_l->socket, this_l->res, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) {
this_l->write_completion_handler (this_l);
});
});
error_response (response_handler, std::string ("Invalid header: ") + header_error.message ());
json_error_response (response_handler, std::string ("Invalid header: ") + header_error.message ());
}
}
void nano::rpc_connection::write_completion_handler (std::shared_ptr<nano::rpc_connection> rpc_connection)
{
// Intentional no-op
}
}

View file

@ -2,26 +2,34 @@
#include <atomic>
#include <boost/beast.hpp>
#include <nano/node/node.hpp>
#include <nano/rpc/rpc_handler.hpp>
namespace nano
{
class rpc;
class logger_mt;
class rpc_config;
class rpc_handler_interface;
class network_constants;
class rpc_connection : public std::enable_shared_from_this<nano::rpc_connection>
{
public:
rpc_connection (nano::node &, nano::rpc &);
rpc_connection (nano::rpc_config const & rpc_config, nano::network_constants const & network_constants, boost::asio::io_context & io_ctx, nano::logger_mt & logger, nano::rpc_handler_interface & rpc_handler_interface_a);
virtual ~rpc_connection () = default;
virtual void parse_connection ();
virtual void write_completion_handler (std::shared_ptr<nano::rpc_connection> rpc_connection);
virtual void prepare_head (unsigned version, boost::beast::http::status status = boost::beast::http::status::ok);
virtual void write_result (std::string body, unsigned version, boost::beast::http::status status = boost::beast::http::status::ok);
void prepare_head (unsigned version, boost::beast::http::status status = boost::beast::http::status::ok);
void write_result (std::string body, unsigned version, boost::beast::http::status status = boost::beast::http::status::ok);
void read ();
std::shared_ptr<nano::node> node;
nano::rpc & rpc;
boost::asio::ip::tcp::socket socket;
boost::beast::flat_buffer buffer;
boost::beast::http::response<boost::beast::http::string_body> res;
std::atomic_flag responded;
boost::asio::io_context & io_ctx;
nano::logger_mt & logger;
nano::rpc_config const & rpc_config;
nano::network_constants const & network_constants;
nano::rpc_handler_interface & rpc_handler_interface;
};
}

View file

@ -3,9 +3,9 @@
#include <boost/polymorphic_pointer_cast.hpp>
nano::rpc_connection_secure::rpc_connection_secure (nano::node & node_a, nano::rpc_secure & rpc_a) :
nano::rpc_connection (node_a, rpc_a),
stream (socket, rpc_a.ssl_context)
nano::rpc_connection_secure::rpc_connection_secure (nano::rpc_config const & rpc_config, nano::network_constants const & network_constants, boost::asio::io_context & io_ctx, nano::logger_mt & logger, nano::rpc_handler_interface & rpc_handler_interface, boost::asio::ssl::context & ssl_context) :
nano::rpc_connection (rpc_config, network_constants, io_ctx, logger, rpc_handler_interface),
stream (socket, ssl_context)
{
}
@ -33,7 +33,7 @@ void nano::rpc_connection_secure::handle_handshake (const boost::system::error_c
}
else
{
node->logger.always_log ("TLS: Handshake error: ", error.message ());
logger.always_log ("TLS: Handshake error: ", error.message ());
}
}

View file

@ -5,7 +5,6 @@
namespace nano
{
class rpc_secure;
/**
* Specialization of nano::rpc_connection for establishing TLS connections.
* Handshakes with client certificates are supported.
@ -13,7 +12,7 @@ class rpc_secure;
class rpc_connection_secure : public rpc_connection
{
public:
rpc_connection_secure (nano::node &, nano::rpc_secure &);
rpc_connection_secure (nano::rpc_config const & rpc_config, nano::network_constants const & network_constants, boost::asio::io_context & io_ctx, nano::logger_mt & logger, nano::rpc_handler_interface & rpc_handler_interface_a, boost::asio::ssl::context & ssl_context);
void parse_connection () override;
void write_completion_handler (std::shared_ptr<nano::rpc_connection> rpc) override;
/** The TLS handshake callback */

File diff suppressed because it is too large Load diff

View file

@ -2,158 +2,28 @@
#include <boost/property_tree/ptree.hpp>
#include <functional>
#include <nano/lib/blocks.hpp>
#include <nano/node/wallet.hpp>
#include <string>
namespace nano
{
class node;
class rpc;
void error_response (std::function<void(boost::property_tree::ptree const &)> response_a, std::string const & message_a);
class rpc_config;
class rpc_handler_interface;
class logger_mt;
class rpc_handler : public std::enable_shared_from_this<nano::rpc_handler>
{
public:
rpc_handler (nano::node &, nano::rpc &, std::string const &, std::string const &, std::function<void(boost::property_tree::ptree const &)> const &);
rpc_handler (nano::rpc_config const & rpc_config, std::string const & body_a, std::string const & request_id_a, std::function<void(std::string const &)> const & response_a, nano::rpc_handler_interface & rpc_handler_interface_a, nano::logger_mt & logger);
void process_request ();
void read (std::shared_ptr<std::vector<uint8_t>> req, std::shared_ptr<std::vector<uint8_t>> res, const std::string & action);
/**
* Process http request
* @param unsafe If true, the caller requests an unsafe action. This will be granted only if the config allows it.
*/
void process_request (bool unsafe = false);
void account_balance ();
void account_block_count ();
void account_count ();
void account_create ();
void account_get ();
void account_history ();
void account_info ();
void account_key ();
void account_list ();
void account_move ();
void account_remove ();
void account_representative ();
void account_representative_set ();
void account_weight ();
void accounts_balances ();
void accounts_create ();
void accounts_frontiers ();
void accounts_pending ();
void active_difficulty ();
void available_supply ();
void block_info ();
void block_confirm ();
void blocks ();
void blocks_info ();
void block_account ();
void block_count ();
void block_count_type ();
void block_create ();
void block_hash ();
void bootstrap ();
void bootstrap_any ();
void bootstrap_lazy ();
void bootstrap_status ();
void chain (bool = false);
void confirmation_active ();
void confirmation_history ();
void confirmation_info ();
void confirmation_quorum ();
void delegators ();
void delegators_count ();
void deterministic_key ();
void frontiers ();
void keepalive ();
void key_create ();
void key_expand ();
void ledger ();
void mnano_to_raw (nano::uint128_t = nano::Mxrb_ratio);
void mnano_from_raw (nano::uint128_t = nano::Mxrb_ratio);
void node_id ();
void node_id_delete ();
void password_change ();
void password_enter ();
void password_valid (bool = false);
void payment_begin ();
void payment_init ();
void payment_end ();
void payment_wait ();
void peers ();
void pending ();
void pending_exists ();
void process ();
void receive ();
void receive_minimum ();
void receive_minimum_set ();
void representatives ();
void representatives_online ();
void republish ();
void search_pending ();
void search_pending_all ();
void send ();
void sign ();
void stats ();
void stats_clear ();
void stop ();
void unchecked ();
void unchecked_clear ();
void unchecked_get ();
void unchecked_keys ();
void unopened ();
void uptime ();
void validate_account_number ();
void version ();
void wallet_add ();
void wallet_add_watch ();
void wallet_balances ();
void wallet_change_seed ();
void wallet_contains ();
void wallet_create ();
void wallet_destroy ();
void wallet_export ();
void wallet_frontiers ();
void wallet_history ();
void wallet_info ();
void wallet_key_valid ();
void wallet_ledger ();
void wallet_lock ();
void wallet_pending ();
void wallet_representative ();
void wallet_representative_set ();
void wallet_republish ();
void wallet_seed ();
void wallet_work_get ();
void work_generate ();
void work_cancel ();
void work_get ();
void work_set ();
void work_validate ();
void work_peer_add ();
void work_peers ();
void work_peers_clear ();
private:
std::string body;
std::string request_id;
nano::node & node;
nano::rpc & rpc;
boost::property_tree::ptree request;
std::function<void(boost::property_tree::ptree const &)> response;
void response_errors ();
std::error_code ec;
boost::property_tree::ptree response_l;
std::shared_ptr<nano::wallet> wallet_impl ();
bool wallet_locked_impl (nano::transaction const &, std::shared_ptr<nano::wallet>);
bool wallet_account_impl (nano::transaction const &, std::shared_ptr<nano::wallet>, nano::account const &);
nano::account account_impl (std::string = "");
nano::amount amount_impl ();
std::shared_ptr<nano::block> block_impl (bool = true);
std::shared_ptr<nano::block> block_json_impl (bool = true);
nano::block_hash hash_impl (std::string = "hash");
nano::amount threshold_optional_impl ();
uint64_t work_optional_impl ();
uint64_t count_impl ();
uint64_t count_optional_impl (uint64_t = std::numeric_limits<uint64_t>::max ());
uint64_t offset_optional_impl (uint64_t = 0);
bool rpc_control_impl ();
std::function<void(std::string const &)> response;
nano::rpc_config const & rpc_config;
nano::rpc_handler_interface & rpc_handler_interface;
nano::logger_mt & logger;
};
}

View file

@ -0,0 +1,178 @@
#include <nano/lib/json_error_response.hpp>
#include <nano/rpc/rpc_request_processor.hpp>
nano::rpc_request_processor::rpc_request_processor (boost::asio::io_context & io_ctx, nano::rpc_config & rpc_config) :
ipc_address (rpc_config.address.to_string ()),
ipc_port (rpc_config.ipc_port),
thread ([this]() {
nano::thread_role::set (nano::thread_role::name::rpc_request_processor);
this->run ();
})
{
std::lock_guard<std::mutex> lk (this->request_mutex);
this->connections.reserve (rpc_config.num_ipc_connections);
for (auto i = 0u; i < rpc_config.num_ipc_connections; ++i)
{
connections.push_back (std::make_shared<nano::ipc_connection> (nano::ipc::ipc_client (io_ctx), false));
auto connection = this->connections.back ();
// clang-format off
connection->client.async_connect (ipc_address, ipc_port, [ connection, &connections_mutex = this->connections_mutex ](nano::error err) {
// Even if there is an error this needs to be set so that another attempt can be made to connect with the ipc connection
std::lock_guard<std::mutex> lk (connections_mutex);
connection->is_available = true;
});
// clang-format on
}
}
nano::rpc_request_processor::~rpc_request_processor ()
{
stop ();
}
void nano::rpc_request_processor::stop ()
{
{
std::lock_guard<std::mutex> lock (request_mutex);
stopped = true;
}
condition.notify_one ();
if (thread.joinable ())
{
thread.join ();
}
}
void nano::rpc_request_processor::add (std::shared_ptr<rpc_request> request)
{
{
std::lock_guard<std::mutex> lk (request_mutex);
requests.push_back (request);
}
condition.notify_one ();
}
void nano::rpc_request_processor::read_payload (std::shared_ptr<nano::ipc_connection> connection, std::shared_ptr<std::vector<uint8_t>> res, std::shared_ptr<nano::rpc_request> rpc_request)
{
uint32_t payload_size_l = boost::endian::big_to_native (*reinterpret_cast<uint32_t *> (res->data ()));
res->resize (payload_size_l);
// Read JSON payload
connection->client.async_read (res, payload_size_l, [this, connection, res, rpc_request](nano::error err_read_a, size_t size_read_a) {
// We need 2 sequential reads to get both the header and payload, so only allow other writes
// when they have both been read.
make_available (*connection);
if (!err_read_a && size_read_a != 0)
{
rpc_request->response (std::string (res->begin (), res->end ()));
if (rpc_request->action == "stop")
{
this->stop_callback ();
}
}
else
{
json_error_response (rpc_request->response, "Failed to read payload");
}
});
}
void nano::rpc_request_processor::make_available (nano::ipc_connection & connection)
{
std::lock_guard<std::mutex> lk (connections_mutex);
connection.is_available = true; // Allow people to use it now
}
// Connection does not exist or has been closed, try to connect to it again and then resend IPC request
void nano::rpc_request_processor::try_reconnect_and_execute_request (std::shared_ptr<nano::ipc_connection> connection, std::shared_ptr<std::vector<uint8_t>> req, std::shared_ptr<std::vector<uint8_t>> res, std::shared_ptr<nano::rpc_request> rpc_request)
{
connection->client.async_connect (ipc_address, ipc_port, [this, connection, req, res, rpc_request](nano::error err) {
if (!err)
{
connection->client.async_write (req, [this, connection, res, rpc_request](nano::error err_a, size_t size_a) {
if (size_a != 0 && !err_a)
{
// Read length
connection->client.async_read (res, sizeof (uint32_t), [this, connection, res, rpc_request](nano::error err_read_a, size_t size_read_a) {
if (size_read_a != 0 && !err_read_a)
{
this->read_payload (connection, res, rpc_request);
}
else
{
json_error_response (rpc_request->response, "Connection to node has failed");
make_available (*connection);
}
});
}
else
{
json_error_response (rpc_request->response, "Cannot write to the node");
make_available (*connection);
}
});
}
else
{
json_error_response (rpc_request->response, "There is a problem connecting to the node. Make sure ipc->tcp is enabled in node config and ports match");
make_available (*connection);
}
});
}
void nano::rpc_request_processor::run ()
{
// This should be a conditioned wait
std::unique_lock<std::mutex> lk (request_mutex);
while (!stopped)
{
if (!requests.empty ())
{
lk.unlock ();
std::unique_lock<std::mutex> conditions_lk (connections_mutex);
// Find the first free ipc_client
auto it = std::find_if (connections.begin (), connections.end (), [](auto connection) -> bool {
return connection->is_available;
});
if (it != connections.cend ())
{
// Successfully found one
lk.lock ();
auto rpc_request = requests.front ();
requests.pop_front ();
lk.unlock ();
auto connection = *it;
connection->is_available = false; // Make sure no one else can take it
conditions_lk.unlock ();
auto req (nano::ipc::prepare_request (nano::ipc::payload_encoding::json_legacy, rpc_request->body));
auto res (std::make_shared<std::vector<uint8_t>> ());
// Have we tried to connect yet?
connection->client.async_write (req, [this, connection, req, res, rpc_request](nano::error err_a, size_t size_a) {
if (!err_a)
{
connection->client.async_read (res, sizeof (uint32_t), [this, connection, req, res, rpc_request](nano::error err_read_a, size_t size_read_a) {
if (size_read_a != 0 && !err_read_a)
{
this->read_payload (connection, res, rpc_request);
}
else
{
this->try_reconnect_and_execute_request (connection, req, res, rpc_request);
}
});
}
else
{
try_reconnect_and_execute_request (connection, req, res, rpc_request);
}
});
}
lk.lock ();
}
else
{
condition.wait (lk);
}
}
}

View file

@ -0,0 +1,92 @@
#pragma once
#include <boost/endian/conversion.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/ipc_client.hpp>
#include <nano/lib/rpc_handler_interface.hpp>
#include <nano/lib/rpcconfig.hpp>
#include <nano/lib/utility.hpp>
#include <nano/rpc/rpc.hpp>
#include <nano/rpc/rpc_handler.hpp>
namespace nano
{
struct ipc_connection
{
ipc_connection (nano::ipc::ipc_client && client_a, bool is_available_a) :
client (std::move (client_a)), is_available (is_available_a)
{
}
nano::ipc::ipc_client client;
bool is_available{ false };
};
struct rpc_request
{
rpc_request (const std::string & action_a, const std::string & body_a, std::function<void(std::string const &)> response_a) :
action (action_a), body (body_a), response (response_a)
{
}
std::string action;
std::string body;
std::function<void(std::string const &)> response;
};
class rpc_request_processor
{
public:
rpc_request_processor (boost::asio::io_context & io_ctx, nano::rpc_config & rpc_config);
~rpc_request_processor ();
void stop ();
void add (std::shared_ptr<rpc_request> request);
std::function<void()> stop_callback;
private:
void run ();
void read_payload (std::shared_ptr<nano::ipc_connection> connection, std::shared_ptr<std::vector<uint8_t>> res, std::shared_ptr<nano::rpc_request> rpc_request);
void try_reconnect_and_execute_request (std::shared_ptr<nano::ipc_connection> connection, std::shared_ptr<std::vector<uint8_t>> req, std::shared_ptr<std::vector<uint8_t>> res, std::shared_ptr<nano::rpc_request> rpc_request);
void make_available (nano::ipc_connection & connection);
std::vector<std::shared_ptr<nano::ipc_connection>> connections;
std::mutex request_mutex;
std::mutex connections_mutex;
bool stopped{ false };
std::deque<std::shared_ptr<nano::rpc_request>> requests;
std::condition_variable condition;
const std::string ipc_address;
const uint16_t ipc_port;
std::thread thread;
};
class ipc_rpc_processor final : public nano::rpc_handler_interface
{
public:
ipc_rpc_processor (boost::asio::io_context & io_ctx, nano::rpc_config & rpc_config) :
rpc_request_processor (io_ctx, rpc_config)
{
}
void process_request (std::string const & action_a, std::string const & body_a, std::function<void(std::string const &)> response_a) override
{
rpc_request_processor.add (std::make_shared<nano::rpc_request> (action_a, body_a, response_a));
}
void stop () override
{
rpc_request_processor.stop ();
}
void rpc_instance (nano::rpc & rpc) override
{
rpc_request_processor.stop_callback = [&rpc]() {
rpc.stop ();
};
}
private:
nano::rpc_request_processor rpc_request_processor;
};
}

View file

@ -1,4 +1,5 @@
#include <nano/node/node.hpp>
#include <boost/format.hpp>
#include <boost/polymorphic_pointer_cast.hpp>
#include <nano/rpc/rpc_connection_secure.hpp>
#include <nano/rpc/rpc_secure.hpp>
@ -9,27 +10,27 @@ bool nano::rpc_secure::on_verify_certificate (bool preverified, boost::asio::ssl
switch (error)
{
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
node.logger.always_log ("TLS: Unable to get issuer");
logger.always_log ("TLS: Unable to get issuer");
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
node.logger.always_log ("TLS: Certificate not yet valid");
logger.always_log ("TLS: Certificate not yet valid");
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
node.logger.always_log ("TLS: Certificate expired");
logger.always_log ("TLS: Certificate expired");
break;
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
if (config.secure.verbose_logging)
{
node.logger.always_log ("TLS: self signed certificate in chain");
logger.always_log ("TLS: self signed certificate in chain");
}
// Allow self-signed certificates
preverified = true;
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
node.logger.always_log ("TLS: Self signed certificate not in the list of trusted certs (forgot to subject-hash certificate filename?)");
logger.always_log ("TLS: Self signed certificate not in the list of trusted certs (forgot to subject-hash certificate filename?)");
break;
default:
break;
@ -39,19 +40,19 @@ bool nano::rpc_secure::on_verify_certificate (bool preverified, boost::asio::ssl
{
if (error != 0)
{
node.logger.always_log ("TLS: Error: ", X509_verify_cert_error_string (error));
node.logger.always_log ("TLS: Error chain depth : ", X509_STORE_CTX_get_error_depth (cts));
logger.always_log ("TLS: Error: ", X509_verify_cert_error_string (error));
logger.always_log ("TLS: Error chain depth : ", X509_STORE_CTX_get_error_depth (cts));
}
X509 * cert = X509_STORE_CTX_get_current_cert (cts);
char subject_name[512];
X509_NAME_oneline (X509_get_subject_name (cert), subject_name, sizeof (subject_name) - 1);
node.logger.always_log ("TLS: Verifying: ", subject_name);
node.logger.always_log ("TLS: Verification: ", preverified);
logger.always_log ("TLS: Verifying: ", subject_name);
logger.always_log ("TLS: Verification: ", preverified);
}
else if (!preverified)
{
node.logger.always_log ("TLS: Pre-verification failed. Turn on verbose logging for more information.");
logger.always_log ("TLS: Pre-verification failed. Turn on verbose logging for more information.");
}
return preverified;
@ -89,8 +90,8 @@ void nano::rpc_secure::load_certs (boost::asio::ssl::context & context_a)
}
}
nano::rpc_secure::rpc_secure (boost::asio::io_service & service_a, nano::node & node_a, nano::rpc_config const & config_a) :
rpc (service_a, node_a, config_a),
nano::rpc_secure::rpc_secure (boost::asio::io_service & service_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a) :
rpc (service_a, config_a, rpc_handler_interface_a),
ssl_context (boost::asio::ssl::context::tlsv12_server)
{
load_certs (ssl_context);
@ -98,7 +99,7 @@ ssl_context (boost::asio::ssl::context::tlsv12_server)
void nano::rpc_secure::accept ()
{
auto connection (std::make_shared<nano::rpc_connection_secure> (node, *this));
auto connection (std::make_shared<nano::rpc_connection_secure> (config, network_constants, io_ctx, logger, rpc_handler_interface, this->ssl_context));
acceptor.async_accept (connection->socket, [this, connection](boost::system::error_code const & ec) {
if (acceptor.is_open ())
{
@ -110,7 +111,7 @@ void nano::rpc_secure::accept ()
}
else
{
this->node.logger.always_log (boost::str (boost::format ("Error accepting RPC connections: %1%") % ec));
logger.always_log (boost::str (boost::format ("Error accepting RPC connections: %1%") % ec));
}
});
}

View file

@ -11,7 +11,7 @@ namespace nano
class rpc_secure : public rpc
{
public:
rpc_secure (boost::asio::io_service & service_a, nano::node & node_a, nano::rpc_config const & config_a);
rpc_secure (boost::asio::io_service & service_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a);
/** Starts accepting connections */
void accept () override;

View file

@ -2,7 +2,7 @@ add_executable (rpc_test
entry.cpp
rpc.cpp)
target_link_libraries(rpc_test gtest rpc)
target_link_libraries(rpc_test gtest rpc node)
target_compile_definitions(rpc_test
PUBLIC

View file

@ -1,7 +1,7 @@
#include <gtest/gtest.h>
#include <nano/secure/utility.hpp>
namespace nano
{
void cleanup_test_directories_on_exit ();
void force_nano_test_network ();
}
@ -10,6 +10,6 @@ int main (int argc, char ** argv)
nano::force_nano_test_network ();
testing::InitGoogleTest (&argc, argv);
auto res = RUN_ALL_TESTS ();
nano::cleanp_test_directories_on_exit ();
nano::cleanup_test_directories_on_exit ();
return res;
}

File diff suppressed because it is too large Load diff

View file

@ -110,16 +110,3 @@ void nano::remove_temporary_directories ()
}
}
}
void nano::cleanp_test_directories_on_exit ()
{
// Makes sure everything is cleaned up
nano::logging::release_file_sink ();
// Clean up tmp directories created by the tests. Since it's sometimes useful to
// see log files after test failures, an environment variable is supported to
// retain the files.
if (std::getenv ("TEST_KEEP_TMPDIRS") == nullptr)
{
nano::remove_temporary_directories ();
}
}

View file

@ -29,5 +29,4 @@ bool migrate_working_path (std::string &);
boost::filesystem::path unique_path ();
// Remove all unique tmp directories created by the process
void remove_temporary_directories ();
void cleanp_test_directories_on_exit ();
}

View file

@ -1,7 +1,7 @@
#include <gtest/gtest.h>
#include <nano/secure/utility.hpp>
namespace nano
{
void cleanup_test_directories_on_exit ();
void force_nano_test_network ();
}
@ -10,6 +10,6 @@ int main (int argc, char ** argv)
nano::force_nano_test_network ();
testing::InitGoogleTest (&argc, argv);
auto res = RUN_ALL_TESTS ();
nano::cleanp_test_directories_on_exit ();
nano::cleanup_test_directories_on_exit ();
return res;
}