From c3046c11d5e12722fc388b8638adc8acf520daed Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Sun, 21 Apr 2019 17:41:36 +0100 Subject: [PATCH] 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 --- .gitignore | 6 + CMakeLists.txt | 15 +- ci/test.sh | 2 +- load-tester/src/launch_node.rs | 125 - load-tester/src/launch_node_and_rpc.rs | 181 + load-tester/src/main.rs | 19 +- nano/core_test/CMakeLists.txt | 4 +- nano/core_test/core_test_main.cc | 4 +- nano/core_test/daemon.cpp | 5 - nano/core_test/ipc.cpp | 6 +- nano/core_test/logger.cpp | 2 - nano/lib/CMakeLists.txt | 2 + nano/lib/config.hpp | 5 + nano/lib/json_error_response.hpp | 16 + nano/lib/rpc_handler_interface.hpp | 15 + nano/lib/rpcconfig.cpp | 98 +- nano/lib/rpcconfig.hpp | 13 +- nano/lib/utility.cpp | 6 + nano/lib/utility.hpp | 4 +- nano/nano_node/CMakeLists.txt | 4 +- nano/nano_node/daemon.cpp | 65 +- nano/nano_node/entry.cpp | 19 +- nano/nano_rpc/CMakeLists.txt | 21 + nano/nano_rpc/entry.cpp | 139 + nano/nano_wallet/entry.cpp | 80 +- nano/node/CMakeLists.txt | 40 +- nano/node/cli.cpp | 2 +- nano/node/daemonconfig.cpp | 11 +- nano/node/daemonconfig.hpp | 14 +- nano/node/ipc.cpp | 38 +- nano/node/ipc.hpp | 10 +- nano/node/json_handler.cpp | 4562 +++++++++++++++++++++ nano/node/json_handler.hpp | 195 + nano/node/json_payment_observer.cpp | 72 + nano/node/json_payment_observer.hpp | 37 + nano/node/logging.cpp | 8 +- nano/node/logging.hpp | 2 - nano/node/node.cpp | 13 +- nano/node/node.hpp | 15 +- nano/node/node_observers.cpp | 13 + nano/node/node_observers.hpp | 24 + nano/node/node_rpc_config.cpp | 78 + nano/node/node_rpc_config.hpp | 26 + nano/node/nodeconfig.cpp | 2 + nano/node/openclconfig.cpp | 24 + nano/node/openclconfig.hpp | 19 + nano/node/openclwork.cpp | 34 +- nano/node/openclwork.hpp | 12 +- nano/node/payment_observer_processor.cpp | 41 + nano/node/payment_observer_processor.hpp | 21 + nano/node/portmapping.hpp | 2 +- nano/node/testing.cpp | 16 + nano/node/transport/transport.hpp | 1 + nano/qt_test/entry.cpp | 4 +- nano/rpc/CMakeLists.txt | 11 +- nano/rpc/rpc.cpp | 158 +- nano/rpc/rpc.hpp | 72 +- nano/rpc/rpc_connection.cpp | 57 +- nano/rpc/rpc_connection.hpp | 22 +- nano/rpc/rpc_connection_secure.cpp | 8 +- nano/rpc/rpc_connection_secure.hpp | 3 +- nano/rpc/rpc_handler.cpp | 4702 +--------------------- nano/rpc/rpc_handler.hpp | 152 +- nano/rpc/rpc_request_processor.cpp | 178 + nano/rpc/rpc_request_processor.hpp | 92 + nano/rpc/rpc_secure.cpp | 31 +- nano/rpc/rpc_secure.hpp | 2 +- nano/rpc_test/CMakeLists.txt | 2 +- nano/rpc_test/entry.cpp | 4 +- nano/rpc_test/rpc.cpp | 1596 ++++++-- nano/secure/utility.cpp | 13 - nano/secure/utility.hpp | 1 - nano/slow_test/entry.cpp | 4 +- 73 files changed, 7551 insertions(+), 5749 deletions(-) delete mode 100644 load-tester/src/launch_node.rs create mode 100644 load-tester/src/launch_node_and_rpc.rs delete mode 100644 nano/core_test/daemon.cpp create mode 100644 nano/lib/json_error_response.hpp create mode 100644 nano/lib/rpc_handler_interface.hpp create mode 100644 nano/nano_rpc/CMakeLists.txt create mode 100644 nano/nano_rpc/entry.cpp create mode 100644 nano/node/json_handler.cpp create mode 100644 nano/node/json_handler.hpp create mode 100644 nano/node/json_payment_observer.cpp create mode 100644 nano/node/json_payment_observer.hpp create mode 100644 nano/node/node_observers.cpp create mode 100644 nano/node/node_observers.hpp create mode 100644 nano/node/node_rpc_config.cpp create mode 100644 nano/node/node_rpc_config.hpp create mode 100644 nano/node/openclconfig.cpp create mode 100644 nano/node/openclconfig.hpp create mode 100644 nano/node/payment_observer_processor.cpp create mode 100644 nano/node/payment_observer_processor.hpp create mode 100644 nano/rpc/rpc_request_processor.cpp create mode 100644 nano/rpc/rpc_request_processor.hpp diff --git a/.gitignore b/.gitignore index 3b45de31..9572aa88 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index d3ca7145..d3f2c32e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/ci/test.sh b/ci/test.sh index 585dc101..7f64be3a 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -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}" diff --git a/load-tester/src/launch_node.rs b/load-tester/src/launch_node.rs deleted file mode 100644 index 8d4ab302..00000000 --- a/load-tester/src/launch_node.rs +++ /dev/null @@ -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( - node: &RpcClient, - i: u64, -) -> Box> { - 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 _ -} diff --git a/load-tester/src/launch_node_and_rpc.rs b/load-tester/src/launch_node_and_rpc.rs new file mode 100644 index 00000000..e2ccdda9 --- /dev/null +++ b/load-tester/src/launch_node_and_rpc.rs @@ -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( + node: &RpcClient, + i: u64, +) -> Box> { + 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 _ +} diff --git a/load-tester/src/main.rs b/load-tester/src/main.rs index ff0fd5c3..2bd546ce 100644 --- a/load-tester/src/main.rs +++ b/load-tester/src/main.rs @@ -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> = 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( ¶ms.node_path, + ¶ms.rpc_path, ¶ms.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())) diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index 9c82c0a3..fbb36b8f 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -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 diff --git a/nano/core_test/core_test_main.cc b/nano/core_test/core_test_main.cc index 06951447..a7ebb4d8 100644 --- a/nano/core_test/core_test_main.cc +++ b/nano/core_test/core_test_main.cc @@ -1,7 +1,7 @@ #include "gtest/gtest.h" -#include 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; } diff --git a/nano/core_test/daemon.cpp b/nano/core_test/daemon.cpp deleted file mode 100644 index fc4c9fb7..00000000 --- a/nano/core_test/daemon.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include - -TEST (daemon, fork) -{ -} \ No newline at end of file diff --git a/nano/core_test/ipc.cpp b/nano/core_test/ipc.cpp index 41adc4b8..04c83cf1 100644 --- a/nano/core_test/ipc.cpp +++ b/nano/core_test/ipc.cpp @@ -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 diff --git a/nano/core_test/logger.cpp b/nano/core_test/logger.cpp index 09e86d27..4d8f09d7 100644 --- a/nano/core_test/logger.cpp +++ b/nano/core_test/logger.cpp @@ -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); diff --git a/nano/lib/CMakeLists.txt b/nano/lib/CMakeLists.txt index 57b06306..31b5c86a 100644 --- a/nano/lib/CMakeLists.txt +++ b/nano/lib/CMakeLists.txt @@ -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 diff --git a/nano/lib/config.hpp b/nano/lib/config.hpp index 12152fa1..646fdcd9 100644 --- a/nano/lib/config.hpp +++ b/nano/lib/config.hpp @@ -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 (); } diff --git a/nano/lib/json_error_response.hpp b/nano/lib/json_error_response.hpp new file mode 100644 index 00000000..315dc917 --- /dev/null +++ b/nano/lib/json_error_response.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace nano +{ +inline void json_error_response (std::function 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 ()); +} +} diff --git a/nano/lib/rpc_handler_interface.hpp b/nano/lib/rpc_handler_interface.hpp new file mode 100644 index 00000000..ebe1c4a7 --- /dev/null +++ b/nano/lib/rpc_handler_interface.hpp @@ -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 response) = 0; + virtual void stop () = 0; + virtual void rpc_instance (nano::rpc & rpc) = 0; +}; +} diff --git a/nano/lib/rpcconfig.cpp b/nano/lib/rpcconfig.cpp index 1ec2c121..956bb6b3 100644 --- a/nano/lib/rpcconfig.cpp +++ b/nano/lib/rpcconfig.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -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 (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 ("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 ("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 ("address", address); + json.get_optional ("port", port); + json.get_optional ("enable_control", enable_control); + json.get_optional ("max_json_depth", max_json_depth); + json.get_optional ("max_request_size", max_request_size); + json.get_optional ("io_threads", io_threads); + json.get_optional ("ipc_port", ipc_port); + json.get_optional ("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 ("address", address); - json.get_optional ("port", port); - json.get_optional ("enable_control", enable_control); - json.get_optional ("max_json_depth", max_json_depth); - json.get_optional ("enable_sign_hash", enable_sign_hash); - json.get_optional ("max_request_size", max_request_size); - std::string max_work_generate_difficulty_text; - json.get_optional ("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 (); +} +} diff --git a/nano/lib/rpcconfig.hpp b/nano/lib/rpcconfig.hpp index ea24a7a9..ff162d2b 100644 --- a/nano/lib/rpcconfig.hpp +++ b/nano/lib/rpcconfig.hpp @@ -1,8 +1,9 @@ #pragma once #include +#include +#include #include -#include #include 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 (); } diff --git a/nano/lib/utility.cpp b/nano/lib/utility.cpp index 44855be9..cc90c2af 100644 --- a/nano/lib/utility.cpp +++ b/nano/lib/utility.cpp @@ -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; } /* diff --git a/nano/lib/utility.hpp b/nano/lib/utility.hpp index ddadf799..8c99cee9 100644 --- a/nano/lib/utility.hpp +++ b/nano/lib/utility.hpp @@ -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 diff --git a/nano/nano_node/CMakeLists.txt b/nano/nano_node/CMakeLists.txt index 4640572f..5d80c402 100644 --- a/nano/nano_node/CMakeLists.txt +++ b/nano/nano_node/CMakeLists.txt @@ -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 diff --git a/nano/nano_node/daemon.cpp b/nano/nano_node/daemon.cpp index a5d2e5db..585d1a02 100644 --- a/nano/nano_node/daemon.cpp +++ b/nano/nano_node/daemon.cpp @@ -5,7 +5,19 @@ #include #include #include +#include +#include +#include #include +#include + +#ifndef BOOST_PROCESS_SUPPORTED +#error BOOST_PROCESS_SUPPORTED must be set, check configuration +#endif + +#if BOOST_PROCESS_SUPPORTED +#include +#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 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 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 rpc_process; +#endif + std::unique_ptr rpc_process_thread; + std::unique_ptr rpc; + std::unique_ptr 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 (*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 (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 ([ 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 (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 { diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index c7210fbe..23ed280a 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -2,10 +2,11 @@ #include #include #include +#include +#include #include +#include #include -#include -#include #include #include @@ -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 (), "Defines the for OpenCL commands") ("device", boost::program_options::value (), "Defines for OpenCL command") ("threads", boost::program_options::value (), "Defines 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 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")) diff --git a/nano/nano_rpc/CMakeLists.txt b/nano/nano_rpc/CMakeLists.txt new file mode 100644 index 00000000..1cb28ab8 --- /dev/null +++ b/nano/nano_rpc/CMakeLists.txt @@ -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}) diff --git a/nano/nano_rpc/entry.cpp b/nano/nano_rpc/entry.cpp new file mode 100644 index 00000000..4313d5f7 --- /dev/null +++ b/nano/nano_rpc/entry.cpp @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 (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 (), "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 ())); + 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 () : 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; +} diff --git a/nano/nano_wallet/entry.cpp b/nano/nano_wallet/entry.cpp index aadd19af..73282def 100644 --- a/nano/nano_wallet/entry.cpp +++ b/nano/nano_wallet/entry.cpp @@ -1,10 +1,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -14,13 +17,18 @@ #include #include +#ifndef BOOST_PROCESS_SUPPORTED +#error BOOST_PROCESS_SUPPORTED must be set, check configuration +#endif + +#if BOOST_PROCESS_SUPPORTED +#include +#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 ("wallet")); auto account_l (json.get ("account")); auto node_l (json.get_required_child ("node")); - rpc_enable = json.get ("rpc_enable"); auto rpc_l (json.get_required_child ("rpc")); + rpc_enable = json.get ("rpc_enable"); opencl_enable = json.get ("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 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 rpc_process; +#endif + std::unique_ptr rpc; + std::unique_ptr 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 (*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 (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 (application, processor, *node, wallet, config.account); diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index d11ab80a..9ecc3a86 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -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 diff --git a/nano/node/cli.cpp b/nano/node/cli.cpp index a2313e97..3c73c7fb 100644 --- a/nano/node/cli.cpp +++ b/nano/node/cli.cpp @@ -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) { diff --git a/nano/node/daemonconfig.cpp b/nano/node/daemonconfig.cpp index 79a99d3f..b26d6937 100644 --- a/nano/node/daemonconfig.cpp +++ b/nano/node/daemonconfig.cpp @@ -1,8 +1,8 @@ +#include #include -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 ("version", version_l); + upgraded_a |= upgrade_json (version_l, json); json.get_optional ("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 ("opencl_enable", opencl_enable); diff --git a/nano/node/daemonconfig.hpp b/nano/node/daemonconfig.hpp index 536f9868..39d4b03a 100644 --- a/nano/node/daemonconfig.hpp +++ b/nano/node/daemonconfig.hpp @@ -1,15 +1,16 @@ #pragma once #include -#include -#include +#include +#include +#include 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; diff --git a/nano/node/ipc.cpp b/nano/node/ipc.cpp index a002afb1..58f8cdfa 100644 --- a/nano/node/ipc.cpp +++ b/nano/node/ipc.cpp @@ -16,9 +16,8 @@ #include #include #include +#include #include -#include -#include #include 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 (this_l->response_body.size ())); std::vector 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 (buffer.data ()), buffer.size ())); - // Note that if the rpc action is async, the shared_ptr lifetime will be extended by the action handler - auto handler (std::make_shared (node, server.rpc, body, request_id_l, response_handler_l)); + // Note that if the rpc action is async, the shared_ptr lifetime will be extended by the action handler + auto handler (std::make_shared (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 { diff --git a/nano/node/ipc.hpp b/nano/node/ipc.hpp index fd1711d3..1b21f6a0 100644 --- a/nano/node/ipc.hpp +++ b/nano/node/ipc.hpp @@ -2,13 +2,12 @@ #include #include -#include -#include +#include +#include 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 id_dispenser{ 0 }; diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp new file mode 100644 index 00000000..c716db00 --- /dev/null +++ b/nano/node/json_handler.cpp @@ -0,0 +1,4562 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +void construct_json (nano::seq_con_info_component * component, boost::property_tree::ptree & parent); +using ipc_json_handler_no_arg_func_map = std::unordered_map>; +ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map (); +auto ipc_json_handler_no_arg_funcs = create_ipc_json_handler_no_arg_func_map (); +} + +nano::json_handler::json_handler (nano::node & node_a, nano::node_rpc_config const & node_rpc_config_a, std::string const & body_a, std::function const & response_a, std::function stop_callback_a) : +body (body_a), +node (node_a), +response (response_a), +stop_callback (stop_callback_a), +node_rpc_config (node_rpc_config_a) +{ +} + +void nano::json_handler::process_request (bool unsafe_a) +{ + try + { + std::stringstream istream (body); + boost::property_tree::read_json (istream, request); + action = request.get ("action"); + auto no_arg_func_iter = ipc_json_handler_no_arg_funcs.find (action); + if (no_arg_func_iter != ipc_json_handler_no_arg_funcs.cend ()) + { + // First try the map of options with no arguments + no_arg_func_iter->second (this); + } + else + { + // Try the rest of the options + if (action == "wallet_seed") + { + if (unsafe_a || node.network_params.network.is_test_network ()) + { + wallet_seed (); + } + else + { + json_error_response (response, "Unsafe RPC not allowed"); + } + } + else if (action == "chain") + { + chain (); + } + else if (action == "successors") + { + chain (true); + } + else if (action == "history") + { + request.put ("head", request.get ("hash")); + account_history (); + } + else if (action == "knano_from_raw" || action == "krai_from_raw") + { + mnano_from_raw (nano::kxrb_ratio); + } + else if (action == "knano_to_raw" || action == "krai_to_raw") + { + mnano_to_raw (nano::kxrb_ratio); + } + else if (action == "nano_from_raw" || action == "rai_from_raw") + { + mnano_from_raw (nano::xrb_ratio); + } + else if (action == "nano_to_raw" || action == "rai_to_raw") + { + mnano_to_raw (nano::xrb_ratio); + } + else if (action == "mnano_from_raw" || action == "mrai_from_raw") + { + mnano_from_raw (); + } + else if (action == "mnano_to_raw" || action == "mrai_to_raw") + { + mnano_to_raw (); + } + else if (action == "password_valid") + { + password_valid (); + } + else if (action == "wallet_locked") + { + password_valid (true); + } + else + { + json_error_response (response, "Unknown command"); + } + } + } + catch (std::runtime_error const &) + { + json_error_response (response, "Unable to parse JSON"); + } + catch (...) + { + json_error_response (response, "Internal server error in RPC"); + } +} + +void nano::json_handler::response_errors () +{ + if (ec || response_l.empty ()) + { + boost::property_tree::ptree response_error; + response_error.put ("error", ec ? ec.message () : "Empty response"); + std::stringstream ostream; + boost::property_tree::write_json (ostream, response_error); + response (ostream.str ()); + } + else + { + std::stringstream ostream; + boost::property_tree::write_json (ostream, response_l); + response (ostream.str ()); + } +} + +std::shared_ptr nano::json_handler::wallet_impl () +{ + if (!ec) + { + std::string wallet_text (request.get ("wallet")); + nano::uint256_union wallet; + if (!wallet.decode_hex (wallet_text)) + { + auto existing (node.wallets.items.find (wallet)); + if (existing != node.wallets.items.end ()) + { + return existing->second; + } + else + { + ec = nano::error_common::wallet_not_found; + } + } + else + { + ec = nano::error_common::bad_wallet_number; + } + } + return nullptr; +} + +bool nano::json_handler::wallet_locked_impl (nano::transaction const & transaction_a, std::shared_ptr wallet_a) +{ + bool result (false); + if (!ec) + { + if (!wallet_a->store.valid_password (transaction_a)) + { + ec = nano::error_common::wallet_locked; + result = true; + } + } + return result; +} + +bool nano::json_handler::wallet_account_impl (nano::transaction const & transaction_a, std::shared_ptr wallet_a, nano::account const & account_a) +{ + bool result (false); + if (!ec) + { + if (wallet_a->store.find (transaction_a, account_a) != wallet_a->store.end ()) + { + result = true; + } + else + { + ec = nano::error_common::account_not_found_wallet; + } + } + return result; +} + +nano::account nano::json_handler::account_impl (std::string account_text) +{ + nano::account result (0); + if (!ec) + { + if (account_text.empty ()) + { + account_text = request.get ("account"); + } + if (result.decode_account (account_text)) + { + ec = nano::error_common::bad_account_number; + } + } + return result; +} + +nano::amount nano::json_handler::amount_impl () +{ + nano::amount result (0); + if (!ec) + { + std::string amount_text (request.get ("amount")); + if (result.decode_dec (amount_text)) + { + ec = nano::error_common::invalid_amount; + } + } + return result; +} + +std::shared_ptr nano::json_handler::block_impl (bool signature_work_required) +{ + std::shared_ptr result; + if (!ec) + { + std::string block_text (request.get ("block")); + boost::property_tree::ptree block_l; + std::stringstream block_stream (block_text); + boost::property_tree::read_json (block_stream, block_l); + if (!signature_work_required) + { + block_l.put ("signature", "0"); + block_l.put ("work", "0"); + } + result = nano::deserialize_block_json (block_l); + if (result == nullptr) + { + ec = nano::error_blocks::invalid_block; + } + } + return result; +} + +std::shared_ptr nano::json_handler::block_json_impl (bool signature_work_required) +{ + std::shared_ptr result; + if (!ec) + { + auto block_l (request.get_child ("block")); + if (!signature_work_required) + { + block_l.put ("signature", "0"); + block_l.put ("work", "0"); + } + result = nano::deserialize_block_json (block_l); + if (result == nullptr) + { + ec = nano::error_blocks::invalid_block; + } + } + return result; +} + +nano::block_hash nano::json_handler::hash_impl (std::string search_text) +{ + nano::block_hash result (0); + if (!ec) + { + std::string hash_text (request.get (search_text)); + if (result.decode_hex (hash_text)) + { + ec = nano::error_blocks::invalid_block_hash; + } + } + return result; +} + +nano::amount nano::json_handler::threshold_optional_impl () +{ + nano::amount result (0); + boost::optional threshold_text (request.get_optional ("threshold")); + if (!ec && threshold_text.is_initialized ()) + { + if (result.decode_dec (threshold_text.get ())) + { + ec = nano::error_common::bad_threshold; + } + } + return result; +} + +uint64_t nano::json_handler::work_optional_impl () +{ + uint64_t result (0); + boost::optional work_text (request.get_optional ("work")); + if (!ec && work_text.is_initialized ()) + { + if (nano::from_string_hex (work_text.get (), result)) + { + ec = nano::error_common::bad_work_format; + } + } + return result; +} + +namespace +{ +bool decode_unsigned (std::string const & text, uint64_t & number) +{ + bool result; + size_t end; + try + { + number = std::stoull (text, &end); + result = false; + } + catch (std::invalid_argument const &) + { + result = true; + } + catch (std::out_of_range const &) + { + result = true; + } + result = result || end != text.size (); + return result; +} +} + +uint64_t nano::json_handler::count_impl () +{ + uint64_t result (0); + if (!ec) + { + std::string count_text (request.get ("count")); + if (decode_unsigned (count_text, result) || result == 0) + { + ec = nano::error_common::invalid_count; + } + } + return result; +} + +uint64_t nano::json_handler::count_optional_impl (uint64_t result) +{ + boost::optional count_text (request.get_optional ("count")); + if (!ec && count_text.is_initialized ()) + { + if (decode_unsigned (count_text.get (), result)) + { + ec = nano::error_common::invalid_count; + } + } + return result; +} + +uint64_t nano::json_handler::offset_optional_impl (uint64_t result) +{ + boost::optional offset_text (request.get_optional ("offset")); + if (!ec && offset_text.is_initialized ()) + { + if (decode_unsigned (offset_text.get (), result)) + { + ec = nano::error_rpc::invalid_offset; + } + } + return result; +} + +void nano::json_handler::account_balance () +{ + auto account (account_impl ()); + if (!ec) + { + auto balance (node.balance_pending (account)); + response_l.put ("balance", balance.first.convert_to ()); + response_l.put ("pending", balance.second.convert_to ()); + } + response_errors (); +} + +void nano::json_handler::account_block_count () +{ + auto account (account_impl ()); + if (!ec) + { + auto transaction (node.store.tx_begin_read ()); + nano::account_info info; + if (!node.store.account_get (transaction, account, info)) + { + response_l.put ("block_count", std::to_string (info.block_count)); + } + else + { + ec = nano::error_common::account_not_found; + } + } + response_errors (); +} + +void nano::json_handler::account_create () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + const bool generate_work = request.get ("work", true); + nano::account new_key; + auto index_text (request.get_optional ("index")); + if (index_text.is_initialized ()) + { + uint64_t index; + if (decode_unsigned (index_text.get (), index) || index > static_cast (std::numeric_limits::max ())) + { + ec = nano::error_common::invalid_index; + } + else + { + new_key = wallet->deterministic_insert (static_cast (index), generate_work); + } + } + else + { + new_key = wallet->deterministic_insert (generate_work); + } + + if (!ec) + { + if (!new_key.is_zero ()) + { + response_l.put ("account", new_key.to_account ()); + } + else + { + ec = nano::error_common::wallet_locked; + } + } + } + response_errors (); +} + +void nano::json_handler::account_get () +{ + std::string key_text (request.get ("key")); + nano::uint256_union pub; + if (!pub.decode_hex (key_text)) + { + response_l.put ("account", pub.to_account ()); + } + else + { + ec = nano::error_common::bad_public_key; + } + response_errors (); +} + +void nano::json_handler::account_info () +{ + auto account (account_impl ()); + if (!ec) + { + const bool representative = request.get ("representative", false); + const bool weight = request.get ("weight", false); + const bool pending = request.get ("pending", false); + auto transaction (node.store.tx_begin_read ()); + nano::account_info info; + if (!node.store.account_get (transaction, account, info)) + { + response_l.put ("frontier", info.head.to_string ()); + response_l.put ("open_block", info.open_block.to_string ()); + response_l.put ("representative_block", info.rep_block.to_string ()); + std::string balance; + nano::uint128_union (info.balance).encode_dec (balance); + response_l.put ("balance", balance); + response_l.put ("modified_timestamp", std::to_string (info.modified)); + response_l.put ("block_count", std::to_string (info.block_count)); + response_l.put ("account_version", info.epoch == nano::epoch::epoch_1 ? "1" : "0"); + response_l.put ("confirmation_height", std::to_string (info.confirmation_height)); + if (representative) + { + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + response_l.put ("representative", block->representative ().to_account ()); + } + if (weight) + { + auto account_weight (node.ledger.weight (transaction, account)); + response_l.put ("weight", account_weight.convert_to ()); + } + if (pending) + { + auto account_pending (node.ledger.account_pending (transaction, account)); + response_l.put ("pending", account_pending.convert_to ()); + } + } + else + { + ec = nano::error_common::account_not_found; + } + } + response_errors (); +} + +void nano::json_handler::account_key () +{ + auto account (account_impl ()); + if (!ec) + { + response_l.put ("key", account.to_string ()); + } + response_errors (); +} + +void nano::json_handler::account_list () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + boost::property_tree::ptree accounts; + auto transaction (node.wallets.tx_begin_read ()); + for (auto i (wallet->store.begin (transaction)), j (wallet->store.end ()); i != j; ++i) + { + boost::property_tree::ptree entry; + entry.put ("", nano::account (i->first).to_account ()); + accounts.push_back (std::make_pair ("", entry)); + } + response_l.add_child ("accounts", accounts); + } + response_errors (); +} + +void nano::json_handler::account_move () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + std::string source_text (request.get ("source")); + auto accounts_text (request.get_child ("accounts")); + nano::uint256_union source; + if (!source.decode_hex (source_text)) + { + auto existing (node.wallets.items.find (source)); + if (existing != node.wallets.items.end ()) + { + auto source (existing->second); + std::vector accounts; + for (auto i (accounts_text.begin ()), n (accounts_text.end ()); i != n; ++i) + { + nano::public_key account; + account.decode_account (i->second.get ("")); + accounts.push_back (account); + } + auto transaction (node.wallets.tx_begin_write ()); + auto error (wallet->store.move (transaction, source->store, accounts)); + response_l.put ("moved", error ? "0" : "1"); + } + else + { + ec = nano::error_rpc::source_not_found; + } + } + else + { + ec = nano::error_rpc::bad_source; + } + } + response_errors (); +} + +void nano::json_handler::account_remove () +{ + auto wallet (wallet_impl ()); + auto account (account_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_write ()); + wallet_locked_impl (transaction, wallet); + wallet_account_impl (transaction, wallet, account); + if (!ec) + { + wallet->store.erase (transaction, account); + response_l.put ("removed", "1"); + } + } + response_errors (); +} + +void nano::json_handler::account_representative () +{ + auto account (account_impl ()); + if (!ec) + { + auto transaction (node.store.tx_begin_read ()); + nano::account_info info; + if (!node.store.account_get (transaction, account, info)) + { + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + response_l.put ("representative", block->representative ().to_account ()); + } + else + { + ec = nano::error_common::account_not_found; + } + } + response_errors (); +} + +void nano::json_handler::account_representative_set () +{ + auto wallet (wallet_impl ()); + auto account (account_impl ()); + if (!ec) + { + std::string representative_text (request.get ("representative")); + nano::account representative; + if (!representative.decode_account (representative_text)) + { + auto work (work_optional_impl ()); + if (!ec && work) + { + auto transaction (node.wallets.tx_begin_write ()); + wallet_locked_impl (transaction, wallet); + wallet_account_impl (transaction, wallet, account); + if (!ec) + { + nano::account_info info; + auto block_transaction (node.store.tx_begin_read ()); + if (!node.store.account_get (block_transaction, account, info)) + { + if (nano::work_validate (info.head, work)) + { + ec = nano::error_common::invalid_work; + } + } + else + { + ec = nano::error_common::account_not_found; + } + } + } + if (!ec) + { + bool generate_work (work == 0); // Disable work generation if "work" option is provided + auto response_a (response); + // clang-format off + wallet->change_async (account, representative, [response_a](std::shared_ptr block) { + if (block != nullptr) + { + boost::property_tree::ptree response_l; + response_l.put ("block", block->hash ().to_string ()); + std::stringstream ostream; + boost::property_tree::write_json (ostream, response_l); + response_a (ostream.str ()); + } + else + { + json_error_response (response_a, "Error generating block"); + } + }, + work, generate_work); + // clang-format on + } + } + else + { + ec = nano::error_rpc::bad_representative_number; + } + } + // Because of change_async + if (ec) + { + response_errors (); + } +} + +void nano::json_handler::account_weight () +{ + auto account (account_impl ()); + if (!ec) + { + auto balance (node.weight (account)); + response_l.put ("weight", balance.convert_to ()); + } + response_errors (); +} + +void nano::json_handler::accounts_balances () +{ + boost::property_tree::ptree balances; + for (auto & accounts : request.get_child ("accounts")) + { + auto account (account_impl (accounts.second.data ())); + if (!ec) + { + boost::property_tree::ptree entry; + auto balance (node.balance_pending (account)); + entry.put ("balance", balance.first.convert_to ()); + entry.put ("pending", balance.second.convert_to ()); + balances.push_back (std::make_pair (account.to_account (), entry)); + } + } + response_l.add_child ("balances", balances); + response_errors (); +} + +void nano::json_handler::accounts_create () +{ + auto wallet (wallet_impl ()); + auto count (count_impl ()); + if (!ec) + { + const bool generate_work = request.get ("work", false); + boost::property_tree::ptree accounts; + for (auto i (0); accounts.size () < count; ++i) + { + nano::account new_key (wallet->deterministic_insert (generate_work)); + if (!new_key.is_zero ()) + { + boost::property_tree::ptree entry; + entry.put ("", new_key.to_account ()); + accounts.push_back (std::make_pair ("", entry)); + } + } + response_l.add_child ("accounts", accounts); + } + response_errors (); +} + +void nano::json_handler::accounts_frontiers () +{ + boost::property_tree::ptree frontiers; + auto transaction (node.store.tx_begin_read ()); + for (auto & accounts : request.get_child ("accounts")) + { + auto account (account_impl (accounts.second.data ())); + if (!ec) + { + auto latest (node.ledger.latest (transaction, account)); + if (!latest.is_zero ()) + { + frontiers.put (account.to_account (), latest.to_string ()); + } + } + } + response_l.add_child ("frontiers", frontiers); + response_errors (); +} + +void nano::json_handler::accounts_pending () +{ + auto count (count_optional_impl ()); + auto threshold (threshold_optional_impl ()); + const bool source = request.get ("source", false); + const bool include_active = request.get ("include_active", false); + const bool sorting = request.get ("sorting", false); + auto simple (threshold.is_zero () && !source && !sorting); // if simple, response is a list of hashes for each account + boost::property_tree::ptree pending; + auto transaction (node.store.tx_begin_read ()); + for (auto & accounts : request.get_child ("accounts")) + { + auto account (account_impl (accounts.second.data ())); + if (!ec) + { + boost::property_tree::ptree peers_l; + for (auto i (node.store.pending_begin (transaction, nano::pending_key (account, 0))); nano::pending_key (i->first).account == account && peers_l.size () < count; ++i) + { + nano::pending_key key (i->first); + if (include_active || node.ledger.block_confirmed (transaction, key.hash)) + { + if (simple) + { + boost::property_tree::ptree entry; + entry.put ("", key.hash.to_string ()); + peers_l.push_back (std::make_pair ("", entry)); + } + else + { + nano::pending_info info (i->second); + if (info.amount.number () >= threshold.number ()) + { + if (source) + { + boost::property_tree::ptree pending_tree; + pending_tree.put ("amount", info.amount.number ().convert_to ()); + pending_tree.put ("source", info.source.to_account ()); + peers_l.add_child (key.hash.to_string (), pending_tree); + } + else + { + peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + } + } + } + } + } + if (sorting && !simple) + { + if (source) + { + peers_l.sort ([](const auto & child1, const auto & child2) -> bool { + return child1.second.template get ("amount") > child2.second.template get ("amount"); + }); + } + else + { + peers_l.sort ([](const auto & child1, const auto & child2) -> bool { + return child1.second.template get ("") > child2.second.template get (""); + }); + } + } + pending.add_child (account.to_account (), peers_l); + } + } + response_l.add_child ("blocks", pending); + response_errors (); +} + +void nano::json_handler::active_difficulty () +{ + response_l.put ("difficulty_threshold", nano::to_string_hex (node.network_params.network.publish_threshold)); + response_l.put ("difficulty_active", nano::to_string_hex (node.active.active_difficulty)); + response_errors (); +} + +void nano::json_handler::available_supply () +{ + auto genesis_balance (node.balance (node.network_params.ledger.genesis_account)); // Cold storage genesis + auto landing_balance (node.balance (nano::account ("059F68AAB29DE0D3A27443625C7EA9CDDB6517A8B76FE37727EF6A4D76832AD5"))); // Active unavailable account + auto faucet_balance (node.balance (nano::account ("8E319CE6F3025E5B2DF66DA7AB1467FE48F1679C13DD43BFDB29FA2E9FC40D3B"))); // Faucet account + auto burned_balance ((node.balance_pending (nano::account (0))).second); // Burning 0 account + auto available (node.network_params.ledger.genesis_amount - genesis_balance - landing_balance - faucet_balance - burned_balance); + response_l.put ("available", available.convert_to ()); + response_errors (); +} + +void state_subtype (nano::transaction const & transaction_a, nano::node & node_a, std::shared_ptr block_a, nano::uint128_t const & balance_a, boost::property_tree::ptree & tree_a) +{ + // Subtype check + auto previous_balance (node_a.ledger.balance (transaction_a, block_a->previous ())); + if (balance_a < previous_balance) + { + tree_a.put ("subtype", "send"); + } + else + { + if (block_a->link ().is_zero ()) + { + tree_a.put ("subtype", "change"); + } + else if (balance_a == previous_balance && !node_a.ledger.epoch_link.is_zero () && node_a.ledger.is_epoch_link (block_a->link ())) + { + tree_a.put ("subtype", "epoch"); + } + else + { + tree_a.put ("subtype", "receive"); + } + } +} + +void nano::json_handler::block_info () +{ + auto hash (hash_impl ()); + if (!ec) + { + nano::block_sideband sideband; + auto transaction (node.store.tx_begin_read ()); + auto block (node.store.block_get (transaction, hash, &sideband)); + if (block != nullptr) + { + nano::account account (block->account ().is_zero () ? sideband.account : block->account ()); + response_l.put ("block_account", account.to_account ()); + auto amount (node.ledger.amount (transaction, hash)); + response_l.put ("amount", amount.convert_to ()); + auto balance (node.ledger.balance (transaction, hash)); + response_l.put ("balance", balance.convert_to ()); + response_l.put ("height", std::to_string (sideband.height)); + response_l.put ("local_timestamp", std::to_string (sideband.timestamp)); + auto confirmed (node.ledger.block_confirmed (transaction, hash)); + response_l.put ("confirmed", confirmed); + + bool json_block_l = request.get ("json_block", false); + if (json_block_l) + { + boost::property_tree::ptree block_node_l; + block->serialize_json (block_node_l); + response_l.add_child ("contents", block_node_l); + } + else + { + std::string contents; + block->serialize_json (contents); + response_l.put ("contents", contents); + } + if (block->type () == nano::block_type::state) + { + state_subtype (transaction, node, block, balance, response_l); + } + } + else + { + ec = nano::error_blocks::not_found; + } + } + response_errors (); +} + +void nano::json_handler::block_confirm () +{ + auto hash (hash_impl ()); + if (!ec) + { + auto transaction (node.store.tx_begin_read ()); + auto block_l (node.store.block_get (transaction, hash)); + if (block_l != nullptr) + { + if (!node.ledger.block_confirmed (transaction, hash)) + { + // Start new confirmation for unconfirmed block + node.block_confirm (std::move (block_l)); + } + else + { + // Add record in confirmation history for confirmed block + nano::election_status status; + status.winner = block_l; + status.tally = 0; + status.election_end = std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()); + status.election_duration = std::chrono::milliseconds::zero (); + { + std::lock_guard lock (node.active.mutex); + node.active.confirmed.push_back (status); + if (node.active.confirmed.size () > node.active.election_history_size) + { + node.active.confirmed.pop_front (); + } + } + // Trigger callback for confirmed block + node.block_arrival.add (hash); + auto account (node.ledger.account (transaction, hash)); + auto amount (node.ledger.amount (transaction, hash)); + bool is_state_send (false); + if (auto state = dynamic_cast (block_l.get ())) + { + is_state_send = node.ledger.is_send (transaction, *state); + } + node.observers.blocks.notify (block_l, account, amount, is_state_send); + } + response_l.put ("started", "1"); + } + else + { + ec = nano::error_blocks::not_found; + } + } + response_errors (); +} + +void nano::json_handler::blocks () +{ + const bool json_block_l = request.get ("json_block", false); + std::vector hashes; + boost::property_tree::ptree blocks; + auto transaction (node.store.tx_begin_read ()); + for (boost::property_tree::ptree::value_type & hashes : request.get_child ("hashes")) + { + if (!ec) + { + std::string hash_text = hashes.second.data (); + nano::uint256_union hash; + if (!hash.decode_hex (hash_text)) + { + auto block (node.store.block_get (transaction, hash)); + if (block != nullptr) + { + if (json_block_l) + { + boost::property_tree::ptree block_node_l; + block->serialize_json (block_node_l); + blocks.add_child (hash_text, block_node_l); + } + else + { + std::string contents; + block->serialize_json (contents); + blocks.put (hash_text, contents); + } + } + else + { + ec = nano::error_blocks::not_found; + } + } + else + { + ec = nano::error_blocks::bad_hash_number; + } + } + } + response_l.add_child ("blocks", blocks); + response_errors (); +} + +void nano::json_handler::blocks_info () +{ + const bool pending = request.get ("pending", false); + const bool source = request.get ("source", false); + const bool json_block_l = request.get ("json_block", false); + + std::vector hashes; + boost::property_tree::ptree blocks; + auto transaction (node.store.tx_begin_read ()); + for (boost::property_tree::ptree::value_type & hashes : request.get_child ("hashes")) + { + if (!ec) + { + std::string hash_text = hashes.second.data (); + nano::uint256_union hash; + if (!hash.decode_hex (hash_text)) + { + nano::block_sideband sideband; + auto block (node.store.block_get (transaction, hash, &sideband)); + if (block != nullptr) + { + boost::property_tree::ptree entry; + nano::account account (block->account ().is_zero () ? sideband.account : block->account ()); + entry.put ("block_account", account.to_account ()); + auto amount (node.ledger.amount (transaction, hash)); + entry.put ("amount", amount.convert_to ()); + auto balance (node.ledger.balance (transaction, hash)); + entry.put ("balance", balance.convert_to ()); + entry.put ("height", std::to_string (sideband.height)); + entry.put ("local_timestamp", std::to_string (sideband.timestamp)); + auto confirmed (node.ledger.block_confirmed (transaction, hash)); + entry.put ("confirmed", confirmed); + + if (json_block_l) + { + boost::property_tree::ptree block_node_l; + block->serialize_json (block_node_l); + entry.add_child ("contents", block_node_l); + } + else + { + std::string contents; + block->serialize_json (contents); + entry.put ("contents", contents); + } + if (block->type () == nano::block_type::state) + { + state_subtype (transaction, node, block, balance, entry); + } + if (pending) + { + bool exists (false); + auto destination (node.ledger.block_destination (transaction, *block)); + if (!destination.is_zero ()) + { + exists = node.store.pending_exists (transaction, nano::pending_key (destination, hash)); + } + entry.put ("pending", exists ? "1" : "0"); + } + if (source) + { + nano::block_hash source_hash (node.ledger.block_source (transaction, *block)); + auto block_a (node.store.block_get (transaction, source_hash)); + if (block_a != nullptr) + { + auto source_account (node.ledger.account (transaction, source_hash)); + entry.put ("source_account", source_account.to_account ()); + } + else + { + entry.put ("source_account", "0"); + } + } + blocks.push_back (std::make_pair (hash_text, entry)); + } + else + { + ec = nano::error_blocks::not_found; + } + } + else + { + ec = nano::error_blocks::bad_hash_number; + } + } + } + response_l.add_child ("blocks", blocks); + response_errors (); +} + +void nano::json_handler::block_account () +{ + auto hash (hash_impl ()); + if (!ec) + { + auto transaction (node.store.tx_begin_read ()); + if (node.store.block_exists (transaction, hash)) + { + auto account (node.ledger.account (transaction, hash)); + response_l.put ("account", account.to_account ()); + } + else + { + ec = nano::error_blocks::not_found; + } + } + response_errors (); +} + +void nano::json_handler::block_count () +{ + auto transaction (node.store.tx_begin_read ()); + response_l.put ("count", std::to_string (node.store.block_count (transaction).sum ())); + response_l.put ("unchecked", std::to_string (node.store.unchecked_count (transaction))); + response_errors (); +} + +void nano::json_handler::block_count_type () +{ + auto transaction (node.store.tx_begin_read ()); + nano::block_counts count (node.store.block_count (transaction)); + response_l.put ("send", std::to_string (count.send)); + response_l.put ("receive", std::to_string (count.receive)); + response_l.put ("open", std::to_string (count.open)); + response_l.put ("change", std::to_string (count.change)); + response_l.put ("state_v0", std::to_string (count.state_v0)); + response_l.put ("state_v1", std::to_string (count.state_v1)); + response_l.put ("state", std::to_string (count.state_v0 + count.state_v1)); + response_errors (); +} + +void nano::json_handler::block_create () +{ + if (!ec) + { + std::string type (request.get ("type")); + nano::uint256_union wallet (0); + boost::optional wallet_text (request.get_optional ("wallet")); + if (wallet_text.is_initialized ()) + { + if (wallet.decode_hex (wallet_text.get ())) + { + ec = nano::error_common::bad_wallet_number; + } + } + nano::uint256_union account (0); + boost::optional account_text (request.get_optional ("account")); + if (!ec && account_text.is_initialized ()) + { + if (account.decode_account (account_text.get ())) + { + ec = nano::error_common::bad_account_number; + } + } + nano::uint256_union representative (0); + boost::optional representative_text (request.get_optional ("representative")); + if (!ec && representative_text.is_initialized ()) + { + if (representative.decode_account (representative_text.get ())) + { + ec = nano::error_rpc::bad_representative_number; + } + } + nano::uint256_union destination (0); + boost::optional destination_text (request.get_optional ("destination")); + if (!ec && destination_text.is_initialized ()) + { + if (destination.decode_account (destination_text.get ())) + { + ec = nano::error_rpc::bad_destination; + } + } + nano::block_hash source (0); + boost::optional source_text (request.get_optional ("source")); + if (!ec && source_text.is_initialized ()) + { + if (source.decode_hex (source_text.get ())) + { + ec = nano::error_rpc::bad_source; + } + } + nano::uint128_union amount (0); + boost::optional amount_text (request.get_optional ("amount")); + if (!ec && amount_text.is_initialized ()) + { + if (amount.decode_dec (amount_text.get ())) + { + ec = nano::error_common::invalid_amount; + } + } + auto work (work_optional_impl ()); + nano::raw_key prv; + prv.data.clear (); + nano::uint256_union previous (0); + nano::uint128_union balance (0); + if (!ec && wallet != 0 && account != 0) + { + auto existing (node.wallets.items.find (wallet)); + if (existing != node.wallets.items.end ()) + { + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + wallet_locked_impl (transaction, existing->second); + wallet_account_impl (transaction, existing->second, account); + if (!ec) + { + existing->second->store.fetch (transaction, account, prv); + previous = node.ledger.latest (block_transaction, account); + balance = node.ledger.account_balance (block_transaction, account); + } + } + else + { + ec = nano::error_common::wallet_not_found; + } + } + boost::optional key_text (request.get_optional ("key")); + if (!ec && key_text.is_initialized ()) + { + if (prv.data.decode_hex (key_text.get ())) + { + ec = nano::error_common::bad_private_key; + } + } + boost::optional previous_text (request.get_optional ("previous")); + if (!ec && previous_text.is_initialized ()) + { + if (previous.decode_hex (previous_text.get ())) + { + ec = nano::error_rpc::bad_previous; + } + } + boost::optional balance_text (request.get_optional ("balance")); + if (!ec && balance_text.is_initialized ()) + { + if (balance.decode_dec (balance_text.get ())) + { + ec = nano::error_rpc::invalid_balance; + } + } + nano::uint256_union link (0); + boost::optional link_text (request.get_optional ("link")); + if (!ec && link_text.is_initialized ()) + { + if (link.decode_account (link_text.get ())) + { + if (link.decode_hex (link_text.get ())) + { + ec = nano::error_rpc::bad_link; + } + } + } + else + { + // Retrieve link from source or destination + link = source.is_zero () ? destination : source; + } + if (prv.data != 0) + { + nano::uint256_union pub (nano::pub_key (prv.data)); + // Fetching account balance & previous for send blocks (if aren't given directly) + if (!previous_text.is_initialized () && !balance_text.is_initialized ()) + { + auto transaction (node.store.tx_begin_read ()); + previous = node.ledger.latest (transaction, pub); + balance = node.ledger.account_balance (transaction, pub); + } + // Double check current balance if previous block is specified + else if (previous_text.is_initialized () && balance_text.is_initialized () && type == "send") + { + auto transaction (node.store.tx_begin_read ()); + if (node.store.block_exists (transaction, previous) && node.store.block_balance (transaction, previous) != balance.number ()) + { + ec = nano::error_rpc::block_create_balance_mismatch; + } + } + // Check for incorrect account key + if (!ec && account_text.is_initialized ()) + { + if (account != pub) + { + ec = nano::error_rpc::block_create_public_key_mismatch; + } + } + if (type == "state") + { + if (previous_text.is_initialized () && !representative.is_zero () && (!link.is_zero () || link_text.is_initialized ())) + { + if (work == 0) + { + work = node.work_generate_blocking (previous.is_zero () ? pub : previous); + } + nano::state_block state (pub, previous, representative, balance, link, prv, pub, work); + response_l.put ("hash", state.hash ().to_string ()); + bool json_block_l = request.get ("json_block", false); + if (json_block_l) + { + boost::property_tree::ptree block_node_l; + state.serialize_json (block_node_l); + response_l.add_child ("block", block_node_l); + } + else + { + std::string contents; + state.serialize_json (contents); + response_l.put ("block", contents); + } + } + else + { + ec = nano::error_rpc::block_create_requirements_state; + } + } + else if (type == "open") + { + if (representative != 0 && source != 0) + { + if (work == 0) + { + work = node.work_generate_blocking (pub); + } + nano::open_block open (source, representative, pub, prv, pub, work); + response_l.put ("hash", open.hash ().to_string ()); + std::string contents; + open.serialize_json (contents); + response_l.put ("block", contents); + } + else + { + ec = nano::error_rpc::block_create_requirements_open; + } + } + else if (type == "receive") + { + if (source != 0 && previous != 0) + { + if (work == 0) + { + work = node.work_generate_blocking (previous); + } + nano::receive_block receive (previous, source, prv, pub, work); + response_l.put ("hash", receive.hash ().to_string ()); + std::string contents; + receive.serialize_json (contents); + response_l.put ("block", contents); + } + else + { + ec = nano::error_rpc::block_create_requirements_receive; + } + } + else if (type == "change") + { + if (representative != 0 && previous != 0) + { + if (work == 0) + { + work = node.work_generate_blocking (previous); + } + nano::change_block change (previous, representative, prv, pub, work); + response_l.put ("hash", change.hash ().to_string ()); + std::string contents; + change.serialize_json (contents); + response_l.put ("block", contents); + } + else + { + ec = nano::error_rpc::block_create_requirements_change; + } + } + else if (type == "send") + { + if (destination != 0 && previous != 0 && balance != 0 && amount != 0) + { + if (balance.number () >= amount.number ()) + { + if (work == 0) + { + work = node.work_generate_blocking (previous); + } + nano::send_block send (previous, destination, balance.number () - amount.number (), prv, pub, work); + response_l.put ("hash", send.hash ().to_string ()); + std::string contents; + send.serialize_json (contents); + response_l.put ("block", contents); + } + else + { + ec = nano::error_common::insufficient_balance; + } + } + else + { + ec = nano::error_rpc::block_create_requirements_send; + } + } + else + { + ec = nano::error_blocks::invalid_type; + } + } + else + { + ec = nano::error_rpc::block_create_key_required; + } + } + response_errors (); +} + +void nano::json_handler::block_hash () +{ + const bool json_block_l = request.get ("json_block", false); + std::shared_ptr block; + if (json_block_l) + { + block = block_json_impl (true); + } + else + { + block = block_impl (true); + } + + if (!ec) + { + response_l.put ("hash", block->hash ().to_string ()); + } + response_errors (); +} + +void nano::json_handler::bootstrap () +{ + std::string address_text = request.get ("address"); + std::string port_text = request.get ("port"); + boost::system::error_code address_ec; + auto address (boost::asio::ip::address_v6::from_string (address_text, address_ec)); + if (!address_ec) + { + uint16_t port; + if (!nano::parse_port (port_text, port)) + { + node.bootstrap_initiator.bootstrap (nano::endpoint (address, port)); + response_l.put ("success", ""); + } + else + { + ec = nano::error_common::invalid_port; + } + } + else + { + ec = nano::error_common::invalid_ip_address; + } + response_errors (); +} + +void nano::json_handler::bootstrap_any () +{ + node.bootstrap_initiator.bootstrap (); + response_l.put ("success", ""); + response_errors (); +} + +void nano::json_handler::bootstrap_lazy () +{ + auto hash (hash_impl ()); + const bool force = request.get ("force", false); + if (!ec) + { + node.bootstrap_initiator.bootstrap_lazy (hash, force); + response_l.put ("started", "1"); + } + response_errors (); +} + +/* + * @warning This is an internal/diagnostic RPC, do not rely on its interface being stable + */ +void nano::json_handler::bootstrap_status () +{ + auto attempt (node.bootstrap_initiator.current_attempt ()); + if (attempt != nullptr) + { + response_l.put ("clients", std::to_string (attempt->clients.size ())); + response_l.put ("pulls", std::to_string (attempt->pulls.size ())); + response_l.put ("pulling", std::to_string (attempt->pulling)); + response_l.put ("connections", std::to_string (attempt->connections)); + response_l.put ("idle", std::to_string (attempt->idle.size ())); + response_l.put ("target_connections", std::to_string (attempt->target_connections (attempt->pulls.size ()))); + response_l.put ("total_blocks", std::to_string (attempt->total_blocks)); + response_l.put ("runs_count", std::to_string (attempt->runs_count)); + std::string mode_text; + if (attempt->mode == nano::bootstrap_mode::legacy) + { + mode_text = "legacy"; + } + else if (attempt->mode == nano::bootstrap_mode::lazy) + { + mode_text = "lazy"; + } + else if (attempt->mode == nano::bootstrap_mode::wallet_lazy) + { + mode_text = "wallet_lazy"; + } + response_l.put ("mode", mode_text); + response_l.put ("lazy_blocks", std::to_string (attempt->lazy_blocks.size ())); + response_l.put ("lazy_state_unknown", std::to_string (attempt->lazy_state_unknown.size ())); + response_l.put ("lazy_balances", std::to_string (attempt->lazy_balances.size ())); + response_l.put ("lazy_pulls", std::to_string (attempt->lazy_pulls.size ())); + response_l.put ("lazy_stopped", std::to_string (attempt->lazy_stopped)); + response_l.put ("lazy_keys", std::to_string (attempt->lazy_keys.size ())); + if (!attempt->lazy_keys.empty ()) + { + response_l.put ("lazy_key_1", (*(attempt->lazy_keys.begin ())).to_string ()); + } + } + else + { + response_l.put ("active", "0"); + } + response_errors (); +} + +void nano::json_handler::chain (bool successors) +{ + successors = successors != request.get ("reverse", false); + auto hash (hash_impl ("block")); + auto count (count_impl ()); + auto offset (offset_optional_impl (0)); + if (!ec) + { + boost::property_tree::ptree blocks; + auto transaction (node.store.tx_begin_read ()); + while (!hash.is_zero () && blocks.size () < count) + { + auto block_l (node.store.block_get (transaction, hash)); + if (block_l != nullptr) + { + if (offset > 0) + { + --offset; + } + else + { + boost::property_tree::ptree entry; + entry.put ("", hash.to_string ()); + blocks.push_back (std::make_pair ("", entry)); + } + hash = successors ? node.store.block_successor (transaction, hash) : block_l->previous (); + } + else + { + hash.clear (); + } + } + response_l.add_child ("blocks", blocks); + } + response_errors (); +} + +void nano::json_handler::confirmation_active () +{ + uint64_t announcements (0); + boost::optional announcements_text (request.get_optional ("announcements")); + if (announcements_text.is_initialized ()) + { + announcements = strtoul (announcements_text.get ().c_str (), NULL, 10); + } + boost::property_tree::ptree elections; + { + std::lock_guard lock (node.active.mutex); + for (auto i (node.active.roots.begin ()), n (node.active.roots.end ()); i != n; ++i) + { + if (i->election->announcements >= announcements && !i->election->confirmed && !i->election->stopped) + { + boost::property_tree::ptree entry; + entry.put ("", i->root.to_string ()); + elections.push_back (std::make_pair ("", entry)); + } + } + } + response_l.add_child ("confirmations", elections); + response_errors (); +} + +void nano::json_handler::confirmation_history () +{ + boost::property_tree::ptree elections; + boost::property_tree::ptree confirmation_stats; + std::chrono::milliseconds running_total (0); + nano::block_hash hash (0); + boost::optional hash_text (request.get_optional ("hash")); + if (hash_text.is_initialized ()) + { + hash = hash_impl (); + } + if (!ec) + { + auto confirmed (node.active.list_confirmed ()); + for (auto i (confirmed.begin ()), n (confirmed.end ()); i != n; ++i) + { + if (hash.is_zero () || i->winner->hash () == hash) + { + boost::property_tree::ptree election; + election.put ("hash", i->winner->hash ().to_string ()); + election.put ("duration", i->election_duration.count ()); + election.put ("time", i->election_end.count ()); + election.put ("tally", i->tally.to_string_dec ()); + elections.push_back (std::make_pair ("", election)); + } + running_total += i->election_duration; + } + } + confirmation_stats.put ("count", elections.size ()); + if (elections.size () >= 1) + { + confirmation_stats.put ("average", (running_total.count ()) / elections.size ()); + } + response_l.add_child ("confirmation_stats", confirmation_stats); + response_l.add_child ("confirmations", elections); + response_errors (); +} + +void nano::json_handler::confirmation_info () +{ + const bool representatives = request.get ("representatives", false); + const bool contents = request.get ("contents", true); + const bool json_block_l = request.get ("json_block", false); + std::string root_text (request.get ("root")); + nano::qualified_root root; + if (!root.decode_hex (root_text)) + { + std::lock_guard lock (node.active.mutex); + auto conflict_info (node.active.roots.find (root)); + if (conflict_info != node.active.roots.end ()) + { + response_l.put ("announcements", std::to_string (conflict_info->election->announcements)); + auto election (conflict_info->election); + nano::uint128_t total (0); + response_l.put ("last_winner", election->status.winner->hash ().to_string ()); + auto transaction (node.store.tx_begin_read ()); + auto tally_l (election->tally (transaction)); + boost::property_tree::ptree blocks; + for (auto i (tally_l.begin ()), n (tally_l.end ()); i != n; ++i) + { + boost::property_tree::ptree entry; + auto tally (i->first); + entry.put ("tally", tally.convert_to ()); + total += tally; + if (contents) + { + if (json_block_l) + { + boost::property_tree::ptree block_node_l; + i->second->serialize_json (block_node_l); + entry.add_child ("contents", block_node_l); + } + else + { + std::string contents; + i->second->serialize_json (contents); + entry.put ("contents", contents); + } + } + if (representatives) + { + std::multimap> representatives; + for (auto ii (election->last_votes.begin ()), nn (election->last_votes.end ()); ii != nn; ++ii) + { + if (i->second->hash () == ii->second.hash) + { + nano::account representative (ii->first); + auto amount (node.store.representation_get (transaction, representative)); + representatives.insert (std::make_pair (amount, representative)); + } + } + boost::property_tree::ptree representatives_list; + for (auto ii (representatives.begin ()), nn (representatives.end ()); ii != nn; ++ii) + { + representatives_list.put (ii->second.to_account (), ii->first.convert_to ()); + } + entry.add_child ("representatives", representatives_list); + } + blocks.add_child ((i->second->hash ()).to_string (), entry); + } + response_l.put ("total_tally", total.convert_to ()); + response_l.add_child ("blocks", blocks); + } + else + { + ec = nano::error_rpc::confirmation_not_found; + } + } + else + { + ec = nano::error_rpc::invalid_root; + } + response_errors (); +} + +void nano::json_handler::confirmation_quorum () +{ + response_l.put ("quorum_delta", node.delta ().convert_to ()); + response_l.put ("online_weight_quorum_percent", std::to_string (node.config.online_weight_quorum)); + response_l.put ("online_weight_minimum", node.config.online_weight_minimum.to_string_dec ()); + response_l.put ("online_stake_total", node.online_reps.online_stake ().convert_to ()); + response_l.put ("peers_stake_total", node.rep_crawler.total_weight ().convert_to ()); + response_l.put ("peers_stake_required", std::max (node.config.online_weight_minimum.number (), node.delta ()).convert_to ()); + if (request.get ("peer_details", false)) + { + boost::property_tree::ptree peers; + for (auto & peer : node.rep_crawler.representatives_by_weight ()) + { + boost::property_tree::ptree peer_node; + peer_node.put ("account", peer.account.to_account ()); + peer_node.put ("ip", peer.channel->to_string ()); + peer_node.put ("weight", peer.weight.to_string_dec ()); + peers.push_back (std::make_pair ("", peer_node)); + } + response_l.add_child ("peers", peers); + } + response_errors (); +} + +void nano::json_handler::delegators () +{ + auto account (account_impl ()); + if (!ec) + { + boost::property_tree::ptree delegators; + auto transaction (node.store.tx_begin_read ()); + for (auto i (node.store.latest_begin (transaction)), n (node.store.latest_end ()); i != n; ++i) + { + nano::account_info info (i->second); + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + if (block->representative () == account) + { + std::string balance; + nano::uint128_union (info.balance).encode_dec (balance); + delegators.put (nano::account (i->first).to_account (), balance); + } + } + response_l.add_child ("delegators", delegators); + } + response_errors (); +} + +void nano::json_handler::delegators_count () +{ + auto account (account_impl ()); + if (!ec) + { + uint64_t count (0); + auto transaction (node.store.tx_begin_read ()); + for (auto i (node.store.latest_begin (transaction)), n (node.store.latest_end ()); i != n; ++i) + { + nano::account_info info (i->second); + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + if (block->representative () == account) + { + ++count; + } + } + response_l.put ("count", std::to_string (count)); + } + response_errors (); +} + +void nano::json_handler::deterministic_key () +{ + std::string seed_text (request.get ("seed")); + std::string index_text (request.get ("index")); + nano::raw_key seed; + if (!seed.data.decode_hex (seed_text)) + { + try + { + uint32_t index (std::stoul (index_text)); + nano::uint256_union prv; + nano::deterministic_key (seed.data, index, prv); + nano::uint256_union pub (nano::pub_key (prv)); + response_l.put ("private", prv.to_string ()); + response_l.put ("public", pub.to_string ()); + response_l.put ("account", pub.to_account ()); + } + catch (std::logic_error const &) + { + ec = nano::error_common::invalid_index; + } + } + else + { + ec = nano::error_common::bad_seed; + } + response_errors (); +} + +void nano::json_handler::frontiers () +{ + auto start (account_impl ()); + auto count (count_impl ()); + if (!ec) + { + boost::property_tree::ptree frontiers; + auto transaction (node.store.tx_begin_read ()); + for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n && frontiers.size () < count; ++i) + { + frontiers.put (nano::account (i->first).to_account (), nano::account_info (i->second).head.to_string ()); + } + response_l.add_child ("frontiers", frontiers); + } + response_errors (); +} + +void nano::json_handler::account_count () +{ + auto transaction (node.store.tx_begin_read ()); + auto size (node.store.account_count (transaction)); + response_l.put ("count", std::to_string (size)); + response_errors (); +} + +namespace +{ +class history_visitor : public nano::block_visitor +{ +public: + history_visitor (nano::json_handler & handler_a, bool raw_a, nano::transaction & transaction_a, boost::property_tree::ptree & tree_a, nano::block_hash const & hash_a, std::vector const & accounts_filter_a = {}) : + handler (handler_a), + raw (raw_a), + transaction (transaction_a), + tree (tree_a), + hash (hash_a), + accounts_filter (accounts_filter_a) + { + } + virtual ~history_visitor () = default; + void send_block (nano::send_block const & block_a) + { + if (should_ignore_account (block_a.hashables.destination)) + { + return; + } + tree.put ("type", "send"); + auto account (block_a.hashables.destination.to_account ()); + tree.put ("account", account); + auto amount (handler.node.ledger.amount (transaction, hash).convert_to ()); + tree.put ("amount", amount); + if (raw) + { + tree.put ("destination", account); + tree.put ("balance", block_a.hashables.balance.to_string_dec ()); + tree.put ("previous", block_a.hashables.previous.to_string ()); + } + } + void receive_block (nano::receive_block const & block_a) + { + if (should_ignore_account (block_a.hashables.source)) + { + return; + } + tree.put ("type", "receive"); + auto account (handler.node.ledger.account (transaction, block_a.hashables.source).to_account ()); + tree.put ("account", account); + auto amount (handler.node.ledger.amount (transaction, hash).convert_to ()); + tree.put ("amount", amount); + if (raw) + { + tree.put ("source", block_a.hashables.source.to_string ()); + tree.put ("previous", block_a.hashables.previous.to_string ()); + } + } + void open_block (nano::open_block const & block_a) + { + if (should_ignore_account (block_a.hashables.source)) + { + return; + } + if (raw) + { + tree.put ("type", "open"); + tree.put ("representative", block_a.hashables.representative.to_account ()); + tree.put ("source", block_a.hashables.source.to_string ()); + tree.put ("opened", block_a.hashables.account.to_account ()); + } + else + { + // Report opens as a receive + tree.put ("type", "receive"); + } + if (block_a.hashables.source != network_params.ledger.genesis_account) + { + tree.put ("account", handler.node.ledger.account (transaction, block_a.hashables.source).to_account ()); + tree.put ("amount", handler.node.ledger.amount (transaction, hash).convert_to ()); + } + else + { + tree.put ("account", network_params.ledger.genesis_account.to_account ()); + tree.put ("amount", network_params.ledger.genesis_amount.convert_to ()); + } + } + void change_block (nano::change_block const & block_a) + { + if (raw && accounts_filter.empty ()) + { + tree.put ("type", "change"); + tree.put ("representative", block_a.hashables.representative.to_account ()); + tree.put ("previous", block_a.hashables.previous.to_string ()); + } + } + void state_block (nano::state_block const & block_a) + { + if (raw) + { + tree.put ("type", "state"); + tree.put ("representative", block_a.hashables.representative.to_account ()); + tree.put ("link", block_a.hashables.link.to_string ()); + tree.put ("balance", block_a.hashables.balance.to_string_dec ()); + tree.put ("previous", block_a.hashables.previous.to_string ()); + } + auto balance (block_a.hashables.balance.number ()); + auto previous_balance (handler.node.ledger.balance (transaction, block_a.hashables.previous)); + if (balance < previous_balance) + { + if (should_ignore_account (block_a.hashables.link)) + { + tree.clear (); + return; + } + if (raw) + { + tree.put ("subtype", "send"); + } + else + { + tree.put ("type", "send"); + } + tree.put ("account", block_a.hashables.link.to_account ()); + tree.put ("amount", (previous_balance - balance).convert_to ()); + } + else + { + if (block_a.hashables.link.is_zero ()) + { + if (raw && accounts_filter.empty ()) + { + tree.put ("subtype", "change"); + } + } + else if (balance == previous_balance && !handler.node.ledger.epoch_link.is_zero () && handler.node.ledger.is_epoch_link (block_a.hashables.link)) + { + if (raw && accounts_filter.empty ()) + { + tree.put ("subtype", "epoch"); + tree.put ("account", handler.node.ledger.epoch_signer.to_account ()); + } + } + else + { + if (should_ignore_account (block_a.hashables.link)) + { + tree.clear (); + return; + } + if (raw) + { + tree.put ("subtype", "receive"); + } + else + { + tree.put ("type", "receive"); + } + tree.put ("account", handler.node.ledger.account (transaction, block_a.hashables.link).to_account ()); + tree.put ("amount", (balance - previous_balance).convert_to ()); + } + } + } + bool should_ignore_account (nano::public_key const & account) + { + bool ignore (false); + if (!accounts_filter.empty ()) + { + if (std::find (accounts_filter.begin (), accounts_filter.end (), account) == accounts_filter.end ()) + { + ignore = true; + } + } + return ignore; + } + nano::json_handler & handler; + bool raw; + nano::transaction & transaction; + boost::property_tree::ptree & tree; + nano::block_hash const & hash; + nano::network_params network_params; + std::vector const & accounts_filter; +}; +} + +void nano::json_handler::account_history () +{ + std::vector accounts_to_filter; + const auto accounts_filter_node = request.get_child_optional ("account_filter"); + if (accounts_filter_node.is_initialized ()) + { + for (auto & a : (*accounts_filter_node)) + { + nano::public_key account; + auto error (account.decode_account (a.second.get (""))); + if (!error) + { + accounts_to_filter.push_back (account); + } + else + { + ec = nano::error_common::bad_account_number; + break; + } + } + } + nano::account account; + nano::block_hash hash; + bool output_raw (request.get_optional ("raw") == true); + bool reverse (request.get_optional ("reverse") == true); + auto head_str (request.get_optional ("head")); + auto transaction (node.store.tx_begin_read ()); + auto count (count_impl ()); + auto offset (offset_optional_impl (0)); + if (head_str) + { + if (!hash.decode_hex (*head_str)) + { + if (node.store.block_exists (transaction, hash)) + { + account = node.ledger.account (transaction, hash); + } + else + { + ec = nano::error_blocks::not_found; + } + } + else + { + ec = nano::error_blocks::bad_hash_number; + } + } + else + { + account = account_impl (); + if (!ec) + { + if (reverse) + { + nano::account_info info; + if (!node.store.account_get (transaction, account, info)) + { + hash = info.open_block; + } + else + { + ec = nano::error_common::account_not_found; + } + } + else + { + hash = node.ledger.latest (transaction, account); + } + } + } + if (!ec) + { + boost::property_tree::ptree history; + response_l.put ("account", account.to_account ()); + nano::block_sideband sideband; + auto block (node.store.block_get (transaction, hash, &sideband)); + while (block != nullptr && count > 0) + { + if (offset > 0) + { + --offset; + } + else + { + boost::property_tree::ptree entry; + history_visitor visitor (*this, output_raw, transaction, entry, hash, accounts_to_filter); + block->visit (visitor); + if (!entry.empty ()) + { + entry.put ("local_timestamp", std::to_string (sideband.timestamp)); + entry.put ("height", std::to_string (sideband.height)); + entry.put ("hash", hash.to_string ()); + if (output_raw) + { + entry.put ("work", nano::to_string_hex (block->block_work ())); + entry.put ("signature", block->block_signature ().to_string ()); + } + history.push_back (std::make_pair ("", entry)); + --count; + } + } + hash = reverse ? node.store.block_successor (transaction, hash) : block->previous (); + block = node.store.block_get (transaction, hash, &sideband); + } + response_l.add_child ("history", history); + if (!hash.is_zero ()) + { + response_l.put (reverse ? "next" : "previous", hash.to_string ()); + } + } + response_errors (); +} + +void nano::json_handler::keepalive () +{ + if (!ec) + { + std::string address_text (request.get ("address")); + std::string port_text (request.get ("port")); + uint16_t port; + if (!nano::parse_port (port_text, port)) + { + node.keepalive (address_text, port); + response_l.put ("started", "1"); + } + else + { + ec = nano::error_common::invalid_port; + } + } + response_errors (); +} + +void nano::json_handler::key_create () +{ + nano::keypair pair; + response_l.put ("private", pair.prv.data.to_string ()); + response_l.put ("public", pair.pub.to_string ()); + response_l.put ("account", pair.pub.to_account ()); + response_errors (); +} + +void nano::json_handler::key_expand () +{ + std::string key_text (request.get ("key")); + nano::uint256_union prv; + if (!prv.decode_hex (key_text)) + { + nano::uint256_union pub (nano::pub_key (prv)); + response_l.put ("private", prv.to_string ()); + response_l.put ("public", pub.to_string ()); + response_l.put ("account", pub.to_account ()); + } + else + { + ec = nano::error_common::bad_private_key; + } + response_errors (); +} + +void nano::json_handler::ledger () +{ + auto count (count_optional_impl ()); + if (!ec) + { + nano::account start (0); + boost::optional account_text (request.get_optional ("account")); + if (account_text.is_initialized ()) + { + if (start.decode_account (account_text.get ())) + { + ec = nano::error_common::bad_account_number; + } + } + uint64_t modified_since (0); + boost::optional modified_since_text (request.get_optional ("modified_since")); + if (modified_since_text.is_initialized ()) + { + if (decode_unsigned (modified_since_text.get (), modified_since)) + { + ec = nano::error_rpc::invalid_timestamp; + } + } + const bool sorting = request.get ("sorting", false); + const bool representative = request.get ("representative", false); + const bool weight = request.get ("weight", false); + const bool pending = request.get ("pending", false); + boost::property_tree::ptree accounts; + auto transaction (node.store.tx_begin_read ()); + if (!ec && !sorting) // Simple + { + for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n && accounts.size () < count; ++i) + { + nano::account_info info (i->second); + if (info.modified >= modified_since) + { + nano::account account (i->first); + boost::property_tree::ptree response_a; + response_a.put ("frontier", info.head.to_string ()); + response_a.put ("open_block", info.open_block.to_string ()); + response_a.put ("representative_block", info.rep_block.to_string ()); + std::string balance; + nano::uint128_union (info.balance).encode_dec (balance); + response_a.put ("balance", balance); + response_a.put ("modified_timestamp", std::to_string (info.modified)); + response_a.put ("block_count", std::to_string (info.block_count)); + if (representative) + { + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + response_a.put ("representative", block->representative ().to_account ()); + } + if (weight) + { + auto account_weight (node.ledger.weight (transaction, account)); + response_a.put ("weight", account_weight.convert_to ()); + } + if (pending) + { + auto account_pending (node.ledger.account_pending (transaction, account)); + response_a.put ("pending", account_pending.convert_to ()); + } + accounts.push_back (std::make_pair (account.to_account (), response_a)); + } + } + } + else if (!ec) // Sorting + { + std::vector> ledger_l; + for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n; ++i) + { + nano::account_info info (i->second); + nano::uint128_union balance (info.balance); + if (info.modified >= modified_since) + { + ledger_l.push_back (std::make_pair (balance, nano::account (i->first))); + } + } + std::sort (ledger_l.begin (), ledger_l.end ()); + std::reverse (ledger_l.begin (), ledger_l.end ()); + nano::account_info info; + for (auto i (ledger_l.begin ()), n (ledger_l.end ()); i != n && accounts.size () < count; ++i) + { + node.store.account_get (transaction, i->second, info); + nano::account account (i->second); + boost::property_tree::ptree response_a; + response_a.put ("frontier", info.head.to_string ()); + response_a.put ("open_block", info.open_block.to_string ()); + response_a.put ("representative_block", info.rep_block.to_string ()); + std::string balance; + (i->first).encode_dec (balance); + response_a.put ("balance", balance); + response_a.put ("modified_timestamp", std::to_string (info.modified)); + response_a.put ("block_count", std::to_string (info.block_count)); + if (representative) + { + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + response_a.put ("representative", block->representative ().to_account ()); + } + if (weight) + { + auto account_weight (node.ledger.weight (transaction, account)); + response_a.put ("weight", account_weight.convert_to ()); + } + if (pending) + { + auto account_pending (node.ledger.account_pending (transaction, account)); + response_a.put ("pending", account_pending.convert_to ()); + } + accounts.push_back (std::make_pair (account.to_account (), response_a)); + } + } + response_l.add_child ("accounts", accounts); + } + response_errors (); +} + +void nano::json_handler::mnano_from_raw (nano::uint128_t ratio) +{ + auto amount (amount_impl ()); + if (!ec) + { + auto result (amount.number () / ratio); + response_l.put ("amount", result.convert_to ()); + } + response_errors (); +} + +void nano::json_handler::mnano_to_raw (nano::uint128_t ratio) +{ + auto amount (amount_impl ()); + if (!ec) + { + auto result (amount.number () * ratio); + if (result > amount.number ()) + { + response_l.put ("amount", result.convert_to ()); + } + else + { + ec = nano::error_common::invalid_amount_big; + } + } + response_errors (); +} + +/* + * @warning This is an internal/diagnostic RPC, do not rely on its interface being stable + */ +void nano::json_handler::node_id () +{ + if (!ec) + { + response_l.put ("private", node.node_id.prv.data.to_string ()); + response_l.put ("public", node.node_id.pub.to_string ()); + response_l.put ("as_account", node.node_id.pub.to_account ()); + } + response_errors (); +} + +/* + * @warning This is an internal/diagnostic RPC, do not rely on its interface being stable + */ +void nano::json_handler::node_id_delete () +{ + response_l.put ("deprecated", "1"); + response_errors (); +} + +void nano::json_handler::password_change () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_write ()); + wallet_locked_impl (transaction, wallet); + if (!ec) + { + std::string password_text (request.get ("password")); + bool error (wallet->store.rekey (transaction, password_text)); + response_l.put ("changed", error ? "0" : "1"); + if (!error) + { + node.logger.try_log ("Wallet password changed"); + } + } + } + response_errors (); +} + +void nano::json_handler::password_enter () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + std::string password_text (request.get ("password")); + auto transaction (wallet->wallets.tx_begin_write ()); + auto error (wallet->enter_password (transaction, password_text)); + response_l.put ("valid", error ? "0" : "1"); + } + response_errors (); +} + +void nano::json_handler::password_valid (bool wallet_locked) +{ + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + auto valid (wallet->store.valid_password (transaction)); + if (!wallet_locked) + { + response_l.put ("valid", valid ? "1" : "0"); + } + else + { + response_l.put ("locked", valid ? "0" : "1"); + } + } + response_errors (); +} + +void nano::json_handler::peers () +{ + boost::property_tree::ptree peers_l; + const bool peer_details = request.get ("peer_details", false); + auto peers_list (node.network.udp_channels.list (std::numeric_limits::max ())); + std::sort (peers_list.begin (), peers_list.end ()); + for (auto i (peers_list.begin ()), n (peers_list.end ()); i != n; ++i) + { + std::stringstream text; + auto channel (*i); + text << channel->to_string (); + if (peer_details) + { + boost::property_tree::ptree pending_tree; + pending_tree.put ("protocol_version", std::to_string (channel->network_version)); + if (channel->node_id.is_initialized ()) + { + pending_tree.put ("node_id", channel->node_id.get ().to_account ()); + } + else + { + pending_tree.put ("node_id", ""); + } + peers_l.push_back (boost::property_tree::ptree::value_type (text.str (), pending_tree)); + } + else + { + peers_l.push_back (boost::property_tree::ptree::value_type (text.str (), boost::property_tree::ptree (std::to_string (channel->network_version)))); + } + } + response_l.add_child ("peers", peers_l); + response_errors (); +} + +void nano::json_handler::pending () +{ + auto account (account_impl ()); + auto count (count_optional_impl ()); + auto threshold (threshold_optional_impl ()); + const bool source = request.get ("source", false); + const bool min_version = request.get ("min_version", false); + const bool include_active = request.get ("include_active", false); + const bool sorting = request.get ("sorting", false); + auto simple (threshold.is_zero () && !source && !min_version && !sorting); // if simple, response is a list of hashes + if (!ec) + { + boost::property_tree::ptree peers_l; + auto transaction (node.store.tx_begin_read ()); + for (auto i (node.store.pending_begin (transaction, nano::pending_key (account, 0))); nano::pending_key (i->first).account == account && peers_l.size () < count; ++i) + { + nano::pending_key key (i->first); + if (include_active || node.ledger.block_confirmed (transaction, key.hash)) + { + if (simple) + { + boost::property_tree::ptree entry; + entry.put ("", key.hash.to_string ()); + peers_l.push_back (std::make_pair ("", entry)); + } + else + { + nano::pending_info info (i->second); + if (info.amount.number () >= threshold.number ()) + { + if (source || min_version) + { + boost::property_tree::ptree pending_tree; + pending_tree.put ("amount", info.amount.number ().convert_to ()); + if (source) + { + pending_tree.put ("source", info.source.to_account ()); + } + if (min_version) + { + pending_tree.put ("min_version", info.epoch == nano::epoch::epoch_1 ? "1" : "0"); + } + peers_l.add_child (key.hash.to_string (), pending_tree); + } + else + { + peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + } + } + } + } + } + if (sorting && !simple) + { + if (source || min_version) + { + peers_l.sort ([](const auto & child1, const auto & child2) -> bool { + return child1.second.template get ("amount") > child2.second.template get ("amount"); + }); + } + else + { + peers_l.sort ([](const auto & child1, const auto & child2) -> bool { + return child1.second.template get ("") > child2.second.template get (""); + }); + } + } + response_l.add_child ("blocks", peers_l); + } + response_errors (); +} + +void nano::json_handler::pending_exists () +{ + auto hash (hash_impl ()); + const bool include_active = request.get ("include_active", false); + if (!ec) + { + auto transaction (node.store.tx_begin_read ()); + auto block (node.store.block_get (transaction, hash)); + if (block != nullptr) + { + auto exists (false); + auto destination (node.ledger.block_destination (transaction, *block)); + if (!destination.is_zero ()) + { + exists = node.store.pending_exists (transaction, nano::pending_key (destination, hash)); + } + exists = exists && (include_active || !node.active.active (*block)); + response_l.put ("exists", exists ? "1" : "0"); + } + else + { + ec = nano::error_blocks::not_found; + } + } + response_errors (); +} + +void nano::json_handler::payment_begin () +{ + std::string id_text (request.get ("wallet")); + nano::uint256_union id; + if (!id.decode_hex (id_text)) + { + auto existing (node.wallets.items.find (id)); + if (existing != node.wallets.items.end ()) + { + auto transaction (node.wallets.tx_begin_write ()); + std::shared_ptr wallet (existing->second); + if (wallet->store.valid_password (transaction)) + { + nano::account account (0); + do + { + auto existing (wallet->free_accounts.begin ()); + if (existing != wallet->free_accounts.end ()) + { + account = *existing; + wallet->free_accounts.erase (existing); + if (wallet->store.find (transaction, account) == wallet->store.end ()) + { + node.logger.always_log (boost::str (boost::format ("Transaction wallet %1% externally modified listing account %2% as free but no longer exists") % id.to_string () % account.to_account ())); + account.clear (); + } + else + { + auto block_transaction (node.store.tx_begin_read ()); + if (!node.ledger.account_balance (block_transaction, account).is_zero ()) + { + node.logger.always_log (boost::str (boost::format ("Skipping account %1% for use as a transaction account: non-zero balance") % account.to_account ())); + account.clear (); + } + } + } + else + { + account = wallet->deterministic_insert (transaction); + break; + } + } while (account.is_zero ()); + if (!account.is_zero ()) + { + response_l.put ("deprecated", "1"); + response_l.put ("account", account.to_account ()); + } + else + { + ec = nano::error_rpc::payment_unable_create_account; + } + } + else + { + ec = nano::error_common::wallet_locked; + } + } + else + { + ec = nano::error_common::wallet_not_found; + } + } + else + { + ec = nano::error_common::bad_wallet_number; + } + response_errors (); +} + +void nano::json_handler::payment_init () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_write ()); + if (wallet->store.valid_password (transaction)) + { + wallet->init_free_accounts (transaction); + response_l.put ("deprecated", "1"); + response_l.put ("status", "Ready"); + } + else + { + ec = nano::error_common::wallet_locked; + } + } + response_errors (); +} + +void nano::json_handler::payment_end () +{ + auto account (account_impl ()); + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + wallet_account_impl (transaction, wallet, account); + if (!ec) + { + if (node.ledger.account_balance (block_transaction, account).is_zero ()) + { + wallet->free_accounts.insert (account); + response_l.put ("deprecated", "1"); + response_l.put ("ended", "1"); + } + else + { + ec = nano::error_rpc::payment_account_balance; + } + } + } + response_errors (); +} + +void nano::json_handler::payment_wait () +{ + std::string timeout_text (request.get ("timeout")); + auto account (account_impl ()); + auto amount (amount_impl ()); + if (!ec) + { + uint64_t timeout; + if (!decode_unsigned (timeout_text, timeout)) + { + { + auto observer (std::make_shared (node, response, account, amount)); + observer->start (timeout); + node.payment_observer_processor.add (account, observer); + } + node.payment_observer_processor.observer_action (account); + } + else + { + ec = nano::error_rpc::bad_timeout; + } + } + if (ec) + { + response_errors (); + } +} + +void nano::json_handler::process () +{ + const bool json_block_l = request.get ("json_block", false); + std::shared_ptr block; + if (json_block_l) + { + block = block_json_impl (true); + } + else + { + block = block_impl (true); + } + + // State blocks subtype check + if (!ec && block->type () == nano::block_type::state) + { + std::string subtype_text (request.get ("subtype", "")); + if (!subtype_text.empty ()) + { + std::shared_ptr block_state (std::static_pointer_cast (block)); + auto transaction (node.store.tx_begin_read ()); + if (!block_state->hashables.previous.is_zero () && !node.store.block_exists (transaction, block_state->hashables.previous)) + { + ec = nano::error_process::gap_previous; + } + else + { + auto balance (node.ledger.account_balance (transaction, block_state->hashables.account)); + if (subtype_text == "send") + { + if (balance <= block_state->hashables.balance.number ()) + { + ec = nano::error_rpc::invalid_subtype_balance; + } + // Send with previous == 0 fails balance check. No previous != 0 check required + } + else if (subtype_text == "receive") + { + if (balance > block_state->hashables.balance.number ()) + { + ec = nano::error_rpc::invalid_subtype_balance; + } + // Receive can be point to open block. No previous != 0 check required + } + else if (subtype_text == "open") + { + if (!block_state->hashables.previous.is_zero ()) + { + ec = nano::error_rpc::invalid_subtype_previous; + } + } + else if (subtype_text == "change") + { + if (balance != block_state->hashables.balance.number ()) + { + ec = nano::error_rpc::invalid_subtype_balance; + } + else if (block_state->hashables.previous.is_zero ()) + { + ec = nano::error_rpc::invalid_subtype_previous; + } + } + else if (subtype_text == "epoch") + { + if (balance != block_state->hashables.balance.number ()) + { + ec = nano::error_rpc::invalid_subtype_balance; + } + else if (!node.ledger.is_epoch_link (block_state->hashables.link)) + { + ec = ec = nano::error_rpc::invalid_subtype_epoch_link; + } + } + else + { + ec = nano::error_rpc::invalid_subtype; + } + } + } + } + if (!ec) + { + if (!nano::work_validate (*block)) + { + auto hash (block->hash ()); + node.block_arrival.add (hash); + nano::process_return result; + { + auto transaction (node.store.tx_begin_write ()); + // Set current time to trigger automatic rebroadcast and election + nano::unchecked_info info (block, block->account (), nano::seconds_since_epoch (), nano::signature_verification::unknown); + result = node.block_processor.process_one (transaction, info); + } + switch (result.code) + { + case nano::process_result::progress: + { + response_l.put ("hash", hash.to_string ()); + break; + } + case nano::process_result::gap_previous: + { + ec = nano::error_process::gap_previous; + break; + } + case nano::process_result::gap_source: + { + ec = nano::error_process::gap_source; + break; + } + case nano::process_result::old: + { + ec = nano::error_process::old; + break; + } + case nano::process_result::bad_signature: + { + ec = nano::error_process::bad_signature; + break; + } + case nano::process_result::negative_spend: + { + // TODO once we get RPC versioning, this should be changed to "negative spend" + ec = nano::error_process::negative_spend; + break; + } + case nano::process_result::balance_mismatch: + { + ec = nano::error_process::balance_mismatch; + break; + } + case nano::process_result::unreceivable: + { + ec = nano::error_process::unreceivable; + break; + } + case nano::process_result::block_position: + { + ec = nano::error_process::block_position; + break; + } + case nano::process_result::fork: + { + const bool force = request.get ("force", false); + if (force) + { + node.active.erase (*block); + node.block_processor.force (block); + response_l.put ("hash", hash.to_string ()); + } + else + { + ec = nano::error_process::fork; + } + break; + } + default: + { + ec = nano::error_process::other; + break; + } + } + } + else + { + ec = nano::error_blocks::work_low; + } + } + response_errors (); +} + +void nano::json_handler::receive () +{ + auto wallet (wallet_impl ()); + auto account (account_impl ()); + auto hash (hash_impl ("block")); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + wallet_locked_impl (transaction, wallet); + wallet_account_impl (transaction, wallet, account); + if (!ec) + { + auto block_transaction (node.store.tx_begin_read ()); + auto block (node.store.block_get (block_transaction, hash)); + if (block != nullptr) + { + if (node.store.pending_exists (block_transaction, nano::pending_key (account, hash))) + { + auto work (work_optional_impl ()); + if (!ec && work) + { + nano::account_info info; + nano::uint256_union head; + if (!node.store.account_get (block_transaction, account, info)) + { + head = info.head; + } + else + { + head = account; + } + if (nano::work_validate (head, work)) + { + ec = nano::error_common::invalid_work; + } + } + if (!ec) + { + bool generate_work (work == 0); // Disable work generation if "work" option is provided + auto response_a (response); + // clang-format off + wallet->receive_async (std::move (block), account, node.network_params.ledger.genesis_amount, [response_a](std::shared_ptr block_a) { + if (block_a != nullptr) + { + boost::property_tree::ptree response_l; + response_l.put ("block", block_a->hash ().to_string ()); + std::stringstream ostream; + boost::property_tree::write_json (ostream, response_l); + response_a (ostream.str ()); + } + else + { + json_error_response (response_a, "Error generating block"); + } + }, + work, generate_work); + // clang-format on + } + } + else + { + ec = nano::error_process::unreceivable; + } + } + else + { + ec = nano::error_blocks::not_found; + } + } + } + // Because of receive_async + if (ec) + { + response_errors (); + } +} + +void nano::json_handler::receive_minimum () +{ + if (!ec) + { + response_l.put ("amount", node.config.receive_minimum.to_string_dec ()); + } + response_errors (); +} + +void nano::json_handler::receive_minimum_set () +{ + auto amount (amount_impl ()); + if (!ec) + { + node.config.receive_minimum = amount; + response_l.put ("success", ""); + } + response_errors (); +} + +void nano::json_handler::representatives () +{ + auto count (count_optional_impl ()); + if (!ec) + { + const bool sorting = request.get ("sorting", false); + boost::property_tree::ptree representatives; + auto transaction (node.store.tx_begin_read ()); + if (!sorting) // Simple + { + for (auto i (node.store.representation_begin (transaction)), n (node.store.representation_end ()); i != n && representatives.size () < count; ++i) + { + nano::account account (i->first); + auto amount (node.store.representation_get (transaction, account)); + representatives.put (account.to_account (), amount.convert_to ()); + } + } + else // Sorting + { + std::vector> representation; + for (auto i (node.store.representation_begin (transaction)), n (node.store.representation_end ()); i != n; ++i) + { + nano::account account (i->first); + auto amount (node.store.representation_get (transaction, account)); + representation.push_back (std::make_pair (amount, account.to_account ())); + } + std::sort (representation.begin (), representation.end ()); + std::reverse (representation.begin (), representation.end ()); + for (auto i (representation.begin ()), n (representation.end ()); i != n && representatives.size () < count; ++i) + { + representatives.put (i->second, (i->first).number ().convert_to ()); + } + } + response_l.add_child ("representatives", representatives); + } + response_errors (); +} + +void nano::json_handler::representatives_online () +{ + const auto accounts_node = request.get_child_optional ("accounts"); + const bool weight = request.get ("weight", false); + std::vector accounts_to_filter; + if (accounts_node.is_initialized ()) + { + for (auto & a : (*accounts_node)) + { + nano::public_key account; + auto error (account.decode_account (a.second.get (""))); + if (!error) + { + accounts_to_filter.push_back (account); + } + else + { + ec = nano::error_common::bad_account_number; + break; + } + } + } + if (!ec) + { + boost::property_tree::ptree representatives; + auto transaction (node.store.tx_begin_read ()); + auto reps (node.online_reps.list ()); + for (auto & i : reps) + { + if (accounts_node.is_initialized ()) + { + if (accounts_to_filter.empty ()) + { + break; + } + auto found_acc = std::find (accounts_to_filter.begin (), accounts_to_filter.end (), i); + if (found_acc == accounts_to_filter.end ()) + { + continue; + } + else + { + accounts_to_filter.erase (found_acc); + } + } + if (weight) + { + boost::property_tree::ptree weight_node; + auto account_weight (node.ledger.weight (transaction, i)); + weight_node.put ("weight", account_weight.convert_to ()); + representatives.add_child (i.to_account (), weight_node); + } + else + { + boost::property_tree::ptree entry; + entry.put ("", i.to_account ()); + representatives.push_back (std::make_pair ("", entry)); + } + } + response_l.add_child ("representatives", representatives); + } + response_errors (); +} + +void nano::json_handler::republish () +{ + auto count (count_optional_impl (1024U)); + uint64_t sources (0); + uint64_t destinations (0); + boost::optional sources_text (request.get_optional ("sources")); + if (!ec && sources_text.is_initialized ()) + { + if (decode_unsigned (sources_text.get (), sources)) + { + ec = nano::error_rpc::invalid_sources; + } + } + boost::optional destinations_text (request.get_optional ("destinations")); + if (!ec && destinations_text.is_initialized ()) + { + if (decode_unsigned (destinations_text.get (), destinations)) + { + ec = nano::error_rpc::invalid_destinations; + } + } + auto hash (hash_impl ()); + if (!ec) + { + boost::property_tree::ptree blocks; + auto transaction (node.store.tx_begin_read ()); + auto block (node.store.block_get (transaction, hash)); + if (block != nullptr) + { + std::deque> republish_bundle; + for (auto i (0); !hash.is_zero () && i < count; ++i) + { + block = node.store.block_get (transaction, hash); + if (sources != 0) // Republish source chain + { + nano::block_hash source (node.ledger.block_source (transaction, *block)); + auto block_a (node.store.block_get (transaction, source)); + std::vector hashes; + while (block_a != nullptr && hashes.size () < sources) + { + hashes.push_back (source); + source = block_a->previous (); + block_a = node.store.block_get (transaction, source); + } + std::reverse (hashes.begin (), hashes.end ()); + for (auto & hash_l : hashes) + { + block_a = node.store.block_get (transaction, hash_l); + republish_bundle.push_back (std::move (block_a)); + boost::property_tree::ptree entry_l; + entry_l.put ("", hash_l.to_string ()); + blocks.push_back (std::make_pair ("", entry_l)); + } + } + republish_bundle.push_back (std::move (block)); // Republish block + boost::property_tree::ptree entry; + entry.put ("", hash.to_string ()); + blocks.push_back (std::make_pair ("", entry)); + if (destinations != 0) // Republish destination chain + { + auto block_b (node.store.block_get (transaction, hash)); + auto destination (node.ledger.block_destination (transaction, *block_b)); + if (!destination.is_zero ()) + { + if (!node.store.pending_exists (transaction, nano::pending_key (destination, hash))) + { + nano::block_hash previous (node.ledger.latest (transaction, destination)); + auto block_d (node.store.block_get (transaction, previous)); + nano::block_hash source; + std::vector hashes; + while (block_d != nullptr && hash != source) + { + hashes.push_back (previous); + source = node.ledger.block_source (transaction, *block_d); + previous = block_d->previous (); + block_d = node.store.block_get (transaction, previous); + } + std::reverse (hashes.begin (), hashes.end ()); + if (hashes.size () > destinations) + { + hashes.resize (destinations); + } + for (auto & hash_l : hashes) + { + block_d = node.store.block_get (transaction, hash_l); + republish_bundle.push_back (std::move (block_d)); + boost::property_tree::ptree entry_l; + entry_l.put ("", hash_l.to_string ()); + blocks.push_back (std::make_pair ("", entry_l)); + } + } + } + } + hash = node.store.block_successor (transaction, hash); + } + node.network.flood_block_batch (republish_bundle, 25); + response_l.put ("success", ""); // obsolete + response_l.add_child ("blocks", blocks); + } + else + { + ec = nano::error_blocks::not_found; + } + } + response_errors (); +} + +void nano::json_handler::search_pending () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + auto error (wallet->search_pending ()); + response_l.put ("started", !error); + } + response_errors (); +} + +void nano::json_handler::search_pending_all () +{ + if (!ec) + { + node.wallets.search_pending_all (); + response_l.put ("success", ""); + } + response_errors (); +} + +void nano::json_handler::send () +{ + auto wallet (wallet_impl ()); + auto amount (amount_impl ()); + // Sending 0 amount is invalid with state blocks + if (!ec && amount.is_zero ()) + { + ec = nano::error_common::invalid_amount; + } + if (!ec) + { + std::string source_text (request.get ("source")); + nano::account source; + if (!source.decode_account (source_text)) + { + std::string destination_text (request.get ("destination")); + nano::account destination; + if (!destination.decode_account (destination_text)) + { + auto work (work_optional_impl ()); + nano::uint128_t balance (0); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + if (wallet->store.valid_password (transaction)) + { + if (wallet->store.find (transaction, source) != wallet->store.end ()) + { + nano::account_info info; + if (!node.store.account_get (block_transaction, source, info)) + { + balance = (info.balance).number (); + } + else + { + ec = nano::error_common::account_not_found; + } + if (!ec && work) + { + if (nano::work_validate (info.head, work)) + { + ec = nano::error_common::invalid_work; + } + } + } + else + { + ec = nano::error_common::account_not_found_wallet; + } + } + else + { + ec = nano::error_common::wallet_locked; + } + } + if (!ec) + { + bool generate_work (work == 0); // Disable work generation if "work" option is provided + boost::optional send_id (request.get_optional ("id")); + auto rpc_l (shared_from_this ()); + auto response_a (response); + // clang-format off + wallet->send_async (source, destination, amount.number (), [balance, amount, response_a](std::shared_ptr block_a) { + if (block_a != nullptr) + { + boost::property_tree::ptree response_l; + response_l.put ("block", block_a->hash ().to_string ()); + std::stringstream ostream; + boost::property_tree::write_json (ostream, response_l); + response_a (ostream.str ()); + } + else + { + if (balance >= amount.number ()) + { + json_error_response (response_a, "Error generating block"); + } + else + { + std::error_code ec (nano::error_common::insufficient_balance); + json_error_response (response_a, ec.message ()); + } + } + }, + work, generate_work, send_id); + // clang-format on + } + } + else + { + ec = nano::error_rpc::bad_destination; + } + } + else + { + ec = nano::error_rpc::bad_source; + } + } + // Because of send_async + if (ec) + { + response_errors (); + } +} + +void nano::json_handler::sign () +{ + const bool json_block_l = request.get ("json_block", false); + // Retrieving hash + nano::block_hash hash (0); + boost::optional hash_text (request.get_optional ("hash")); + if (hash_text.is_initialized ()) + { + hash = hash_impl (); + } + // Retrieving block + std::shared_ptr block; + boost::optional block_text (request.get_optional ("block")); + if (!ec && block_text.is_initialized ()) + { + if (json_block_l) + { + block = block_json_impl (true); + } + else + { + block = block_impl (true); + } + if (block != nullptr) + { + hash = block->hash (); + } + } + + // Hash or block are not initialized + if (!ec && hash.is_zero ()) + { + ec = nano::error_blocks::invalid_block; + } + // Hash is initialized without config permission + else if (!ec && !hash.is_zero () && block == nullptr && !node_rpc_config.enable_sign_hash) + { + ec = nano::error_rpc::sign_hash_disabled; + } + if (!ec) + { + nano::raw_key prv; + prv.data.clear (); + // Retrieving private key from request + boost::optional key_text (request.get_optional ("key")); + if (key_text.is_initialized ()) + { + if (prv.data.decode_hex (key_text.get ())) + { + ec = nano::error_common::bad_private_key; + } + } + else + { + // Retrieving private key from wallet + boost::optional account_text (request.get_optional ("account")); + boost::optional wallet_text (request.get_optional ("wallet")); + if (wallet_text.is_initialized () && account_text.is_initialized ()) + { + auto account (account_impl ()); + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + wallet_locked_impl (transaction, wallet); + wallet_account_impl (transaction, wallet, account); + if (!ec) + { + wallet->store.fetch (transaction, account, prv); + } + } + } + } + // Signing + if (prv.data != 0) + { + nano::public_key pub (nano::pub_key (prv.data)); + nano::signature signature (nano::sign_message (prv, pub, hash)); + response_l.put ("signature", signature.to_string ()); + if (block != nullptr) + { + block->signature_set (signature); + + if (json_block_l) + { + boost::property_tree::ptree block_node_l; + block->serialize_json (block_node_l); + response_l.add_child ("block", block_node_l); + } + else + { + std::string contents; + block->serialize_json (contents); + response_l.put ("block", contents); + } + } + } + else + { + ec = nano::error_rpc::block_create_key_required; + } + } + response_errors (); +} + +void nano::json_handler::stats () +{ + auto sink = node.stats.log_sink_json (); + std::string type (request.get ("type", "")); + bool use_sink = false; + if (type == "counters") + { + node.stats.log_counters (*sink); + use_sink = true; + } + else if (type == "objects") + { + construct_json (collect_seq_con_info (node, "node").get (), response_l); + } + else if (type == "samples") + { + node.stats.log_samples (*sink); + use_sink = true; + } + else + { + ec = nano::error_rpc::invalid_missing_type; + } + if (!ec && use_sink) + { + auto stat_tree_l (*static_cast (sink->to_object ())); + stat_tree_l.put ("stat_duration_seconds", node.stats.last_reset ().count ()); + std::stringstream ostream; + boost::property_tree::write_json (ostream, stat_tree_l); + response (ostream.str ()); + } + else + { + response_errors (); + } +} + +void nano::json_handler::stats_clear () +{ + node.stats.clear (); + response_l.put ("success", ""); + std::stringstream ostream; + boost::property_tree::write_json (ostream, response_l); + response (ostream.str ()); +} + +void nano::json_handler::stop () +{ + response_l.put ("success", ""); + response_errors (); + if (!ec) + { + node.stop (); + stop_callback (); + } +} + +void nano::json_handler::unchecked () +{ + auto count (count_optional_impl ()); + if (!ec) + { + boost::property_tree::ptree unchecked; + auto transaction (node.store.tx_begin_read ()); + for (auto i (node.store.unchecked_begin (transaction)), n (node.store.unchecked_end ()); i != n && unchecked.size () < count; ++i) + { + nano::unchecked_info info (i->second); + std::string contents; + info.block->serialize_json (contents); + unchecked.put (info.block->hash ().to_string (), contents); + } + response_l.add_child ("blocks", unchecked); + } + response_errors (); +} + +void nano::json_handler::unchecked_clear () +{ + auto transaction (node.store.tx_begin_write ()); + node.store.unchecked_clear (transaction); + response_l.put ("success", ""); + response_errors (); +} + +void nano::json_handler::unchecked_get () +{ + const bool json_block_l = request.get ("json_block", false); + auto hash (hash_impl ()); + if (!ec) + { + auto transaction (node.store.tx_begin_read ()); + for (auto i (node.store.unchecked_begin (transaction)), n (node.store.unchecked_end ()); i != n; ++i) + { + nano::unchecked_key key (i->first); + if (key.hash == hash) + { + nano::unchecked_info info (i->second); + response_l.put ("modified_timestamp", std::to_string (info.modified)); + + if (json_block_l) + { + boost::property_tree::ptree block_node_l; + info.block->serialize_json (block_node_l); + response_l.add_child ("contents", block_node_l); + } + else + { + std::string contents; + info.block->serialize_json (contents); + response_l.put ("contents", contents); + } + break; + } + } + if (response_l.empty ()) + { + ec = nano::error_blocks::not_found; + } + } + response_errors (); +} + +void nano::json_handler::unchecked_keys () +{ + const bool json_block_l = request.get ("json_block", false); + auto count (count_optional_impl ()); + nano::uint256_union key (0); + boost::optional hash_text (request.get_optional ("key")); + if (!ec && hash_text.is_initialized ()) + { + if (key.decode_hex (hash_text.get ())) + { + ec = nano::error_rpc::bad_key; + } + } + if (!ec) + { + boost::property_tree::ptree unchecked; + auto transaction (node.store.tx_begin_read ()); + for (auto i (node.store.unchecked_begin (transaction, nano::unchecked_key (key, 0))), n (node.store.unchecked_end ()); i != n && unchecked.size () < count; ++i) + { + boost::property_tree::ptree entry; + nano::unchecked_info info (i->second); + entry.put ("key", nano::block_hash (i->first.key ()).to_string ()); + entry.put ("hash", info.block->hash ().to_string ()); + entry.put ("modified_timestamp", std::to_string (info.modified)); + if (json_block_l) + { + boost::property_tree::ptree block_node_l; + info.block->serialize_json (block_node_l); + entry.add_child ("contents", block_node_l); + } + else + { + std::string contents; + info.block->serialize_json (contents); + entry.put ("contents", contents); + } + unchecked.push_back (std::make_pair ("", entry)); + } + response_l.add_child ("unchecked", unchecked); + } + response_errors (); +} + +void nano::json_handler::unopened () +{ + auto count (count_optional_impl ()); + nano::account start (1); // exclude burn account by default + boost::optional account_text (request.get_optional ("account")); + if (account_text.is_initialized ()) + { + if (start.decode_account (account_text.get ())) + { + ec = nano::error_common::bad_account_number; + } + } + if (!ec) + { + auto transaction (node.store.tx_begin_read ()); + auto iterator (node.store.pending_begin (transaction, nano::pending_key (start, 0))); + auto end (node.store.pending_end ()); + nano::account current_account (start); + nano::uint128_t current_account_sum{ 0 }; + boost::property_tree::ptree accounts; + while (iterator != end && accounts.size () < count) + { + nano::pending_key key (iterator->first); + nano::account account (key.account); + nano::pending_info info (iterator->second); + if (node.store.account_exists (transaction, account)) + { + if (account.number () == std::numeric_limits::max ()) + { + break; + } + // Skip existing accounts + iterator = node.store.pending_begin (transaction, nano::pending_key (account.number () + 1, 0)); + } + else + { + if (account != current_account) + { + if (current_account_sum > 0) + { + accounts.put (current_account.to_account (), current_account_sum.convert_to ()); + current_account_sum = 0; + } + current_account = account; + } + current_account_sum += info.amount.number (); + ++iterator; + } + } + // last one after iterator reaches end + if (current_account_sum > 0 && accounts.size () < count) + { + accounts.put (current_account.to_account (), current_account_sum.convert_to ()); + } + response_l.add_child ("accounts", accounts); + } + response_errors (); +} + +void nano::json_handler::uptime () +{ + response_l.put ("seconds", std::chrono::duration_cast (std::chrono::steady_clock::now () - node.startup_time).count ()); + response_errors (); +} + +void nano::json_handler::version () +{ + response_l.put ("rpc_version", "1"); + response_l.put ("store_version", std::to_string (node.store_version ())); + response_l.put ("protocol_version", std::to_string (nano::protocol_version)); + if (NANO_VERSION_PATCH == 0) + { + response_l.put ("node_vendor", boost::str (boost::format ("Nano %1%") % NANO_MAJOR_MINOR_VERSION)); + } + else + { + response_l.put ("node_vendor", boost::str (boost::format ("Nano %1%") % NANO_MAJOR_MINOR_RC_VERSION)); + } + response_errors (); +} + +void nano::json_handler::validate_account_number () +{ + std::string account_text (request.get ("account")); + nano::uint256_union account; + auto error (account.decode_account (account_text)); + response_l.put ("valid", error ? "0" : "1"); + response_errors (); +} + +void nano::json_handler::wallet_add () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + std::string key_text (request.get ("key")); + nano::raw_key key; + if (!key.data.decode_hex (key_text)) + { + const bool generate_work = request.get ("work", true); + auto pub (wallet->insert_adhoc (key, generate_work)); + if (!pub.is_zero ()) + { + response_l.put ("account", pub.to_account ()); + } + else + { + ec = nano::error_common::wallet_locked; + } + } + else + { + ec = nano::error_common::bad_private_key; + } + } + response_errors (); +} + +void nano::json_handler::wallet_add_watch () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_write ()); + if (wallet->store.valid_password (transaction)) + { + for (auto & accounts : request.get_child ("accounts")) + { + auto account (account_impl (accounts.second.data ())); + if (!ec) + { + wallet->insert_watch (transaction, account); + } + } + response_l.put ("success", ""); + } + else + { + ec = nano::error_common::wallet_locked; + } + } + response_errors (); +} + +void nano::json_handler::wallet_info () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + nano::uint128_t balance (0); + nano::uint128_t pending (0); + uint64_t count (0); + uint64_t deterministic_count (0); + uint64_t adhoc_count (0); + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) + { + nano::account account (i->first); + balance = balance + node.ledger.account_balance (block_transaction, account); + pending = pending + node.ledger.account_pending (block_transaction, account); + nano::key_type key_type (wallet->store.key_type (i->second)); + if (key_type == nano::key_type::deterministic) + { + deterministic_count++; + } + else if (key_type == nano::key_type::adhoc) + { + adhoc_count++; + } + count++; + } + uint32_t deterministic_index (wallet->store.deterministic_index_get (transaction)); + response_l.put ("balance", balance.convert_to ()); + response_l.put ("pending", pending.convert_to ()); + response_l.put ("accounts_count", std::to_string (count)); + response_l.put ("deterministic_count", std::to_string (deterministic_count)); + response_l.put ("adhoc_count", std::to_string (adhoc_count)); + response_l.put ("deterministic_index", std::to_string (deterministic_index)); + } + response_errors (); +} + +void nano::json_handler::wallet_balances () +{ + auto wallet (wallet_impl ()); + auto threshold (threshold_optional_impl ()); + if (!ec) + { + boost::property_tree::ptree balances; + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) + { + nano::account account (i->first); + nano::uint128_t balance = node.ledger.account_balance (block_transaction, account); + if (balance >= threshold.number ()) + { + boost::property_tree::ptree entry; + nano::uint128_t pending = node.ledger.account_pending (block_transaction, account); + entry.put ("balance", balance.convert_to ()); + entry.put ("pending", pending.convert_to ()); + balances.push_back (std::make_pair (account.to_account (), entry)); + } + } + response_l.add_child ("balances", balances); + } + response_errors (); +} + +void nano::json_handler::wallet_change_seed () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + std::string seed_text (request.get ("seed")); + nano::raw_key seed; + if (!seed.data.decode_hex (seed_text)) + { + auto count (static_cast (count_optional_impl (0))); + auto transaction (node.wallets.tx_begin_write ()); + if (wallet->store.valid_password (transaction)) + { + nano::public_key account (wallet->change_seed (transaction, seed, count)); + response_l.put ("success", ""); + response_l.put ("last_restored_account", account.to_account ()); + auto index (wallet->store.deterministic_index_get (transaction)); + assert (index > 0); + response_l.put ("restored_count", std::to_string (index)); + } + else + { + ec = nano::error_common::wallet_locked; + } + } + else + { + ec = nano::error_common::bad_seed; + } + } + response_errors (); +} + +void nano::json_handler::wallet_contains () +{ + auto account (account_impl ()); + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + auto exists (wallet->store.find (transaction, account) != wallet->store.end ()); + response_l.put ("exists", exists ? "1" : "0"); + } + response_errors (); +} + +void nano::json_handler::wallet_create () +{ + nano::raw_key seed; + auto seed_text (request.get_optional ("seed")); + if (seed_text.is_initialized () && seed.data.decode_hex (seed_text.get ())) + { + ec = nano::error_common::bad_seed; + } + if (!ec) + { + nano::keypair wallet_id; + auto wallet (node.wallets.create (wallet_id.pub)); + auto existing (node.wallets.items.find (wallet_id.pub)); + if (existing != node.wallets.items.end ()) + { + response_l.put ("wallet", wallet_id.pub.to_string ()); + } + else + { + ec = nano::error_common::wallet_lmdb_max_dbs; + } + if (!ec && seed_text.is_initialized ()) + { + auto transaction (node.wallets.tx_begin_write ()); + nano::public_key account (wallet->change_seed (transaction, seed)); + response_l.put ("last_restored_account", account.to_account ()); + auto index (wallet->store.deterministic_index_get (transaction)); + assert (index > 0); + response_l.put ("restored_count", std::to_string (index)); + } + } + response_errors (); +} + +void nano::json_handler::wallet_destroy () +{ + std::string wallet_text (request.get ("wallet")); + nano::uint256_union wallet; + if (!wallet.decode_hex (wallet_text)) + { + auto existing (node.wallets.items.find (wallet)); + if (existing != node.wallets.items.end ()) + { + node.wallets.destroy (wallet); + bool destroyed (node.wallets.items.find (wallet) == node.wallets.items.end ()); + response_l.put ("destroyed", destroyed ? "1" : "0"); + } + else + { + ec = nano::error_common::wallet_not_found; + } + } + else + { + ec = nano::error_common::bad_wallet_number; + } + response_errors (); +} + +void nano::json_handler::wallet_export () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + std::string json; + wallet->store.serialize_json (transaction, json); + response_l.put ("json", json); + } + response_errors (); +} + +void nano::json_handler::wallet_frontiers () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + boost::property_tree::ptree frontiers; + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) + { + nano::account account (i->first); + auto latest (node.ledger.latest (block_transaction, account)); + if (!latest.is_zero ()) + { + frontiers.put (account.to_account (), latest.to_string ()); + } + } + response_l.add_child ("frontiers", frontiers); + } + response_errors (); +} + +void nano::json_handler::wallet_history () +{ + uint64_t modified_since (1); + boost::optional modified_since_text (request.get_optional ("modified_since")); + if (modified_since_text.is_initialized ()) + { + if (decode_unsigned (modified_since_text.get (), modified_since)) + { + ec = nano::error_rpc::invalid_timestamp; + } + } + auto wallet (wallet_impl ()); + if (!ec) + { + std::multimap> entries; + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) + { + nano::account account (i->first); + nano::account_info info; + if (!node.store.account_get (block_transaction, account, info)) + { + auto timestamp (info.modified); + auto hash (info.head); + while (timestamp >= modified_since && !hash.is_zero ()) + { + nano::block_sideband sideband; + auto block (node.store.block_get (block_transaction, hash, &sideband)); + timestamp = sideband.timestamp; + if (block != nullptr && timestamp >= modified_since) + { + boost::property_tree::ptree entry; + history_visitor visitor (*this, false, block_transaction, entry, hash); + block->visit (visitor); + if (!entry.empty ()) + { + entry.put ("block_account", account.to_account ()); + entry.put ("hash", hash.to_string ()); + entry.put ("local_timestamp", std::to_string (timestamp)); + entries.insert (std::make_pair (timestamp, entry)); + } + hash = block->previous (); + } + else + { + hash.clear (); + } + } + } + } + boost::property_tree::ptree history; + for (auto i (entries.begin ()), n (entries.end ()); i != n; ++i) + { + history.push_back (std::make_pair ("", i->second)); + } + response_l.add_child ("history", history); + } + response_errors (); +} + +void nano::json_handler::wallet_key_valid () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + auto valid (wallet->store.valid_password (transaction)); + response_l.put ("valid", valid ? "1" : "0"); + } + response_errors (); +} + +void nano::json_handler::wallet_ledger () +{ + const bool representative = request.get ("representative", false); + const bool weight = request.get ("weight", false); + const bool pending = request.get ("pending", false); + uint64_t modified_since (0); + boost::optional modified_since_text (request.get_optional ("modified_since")); + if (modified_since_text.is_initialized ()) + { + modified_since = strtoul (modified_since_text.get ().c_str (), NULL, 10); + } + auto wallet (wallet_impl ()); + if (!ec) + { + boost::property_tree::ptree accounts; + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) + { + nano::account account (i->first); + nano::account_info info; + if (!node.store.account_get (block_transaction, account, info)) + { + if (info.modified >= modified_since) + { + boost::property_tree::ptree entry; + entry.put ("frontier", info.head.to_string ()); + entry.put ("open_block", info.open_block.to_string ()); + entry.put ("representative_block", info.rep_block.to_string ()); + std::string balance; + nano::uint128_union (info.balance).encode_dec (balance); + entry.put ("balance", balance); + entry.put ("modified_timestamp", std::to_string (info.modified)); + entry.put ("block_count", std::to_string (info.block_count)); + if (representative) + { + auto block (node.store.block_get (block_transaction, info.rep_block)); + assert (block != nullptr); + entry.put ("representative", block->representative ().to_account ()); + } + if (weight) + { + auto account_weight (node.ledger.weight (block_transaction, account)); + entry.put ("weight", account_weight.convert_to ()); + } + if (pending) + { + auto account_pending (node.ledger.account_pending (block_transaction, account)); + entry.put ("pending", account_pending.convert_to ()); + } + accounts.push_back (std::make_pair (account.to_account (), entry)); + } + } + } + response_l.add_child ("accounts", accounts); + } + response_errors (); +} + +void nano::json_handler::wallet_lock () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + nano::raw_key empty; + empty.data.clear (); + wallet->store.password.value_set (empty); + response_l.put ("locked", "1"); + node.logger.try_log ("Wallet locked"); + } + response_errors (); +} + +void nano::json_handler::wallet_pending () +{ + auto wallet (wallet_impl ()); + auto count (count_optional_impl ()); + auto threshold (threshold_optional_impl ()); + const bool source = request.get ("source", false); + const bool min_version = request.get ("min_version", false); + const bool include_active = request.get ("include_active", false); + if (!ec) + { + boost::property_tree::ptree pending; + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) + { + nano::account account (i->first); + boost::property_tree::ptree peers_l; + for (auto ii (node.store.pending_begin (block_transaction, nano::pending_key (account, 0))); nano::pending_key (ii->first).account == account && peers_l.size () < count; ++ii) + { + nano::pending_key key (ii->first); + if (include_active || node.ledger.block_confirmed (block_transaction, key.hash)) + { + if (threshold.is_zero () && !source) + { + boost::property_tree::ptree entry; + entry.put ("", key.hash.to_string ()); + peers_l.push_back (std::make_pair ("", entry)); + } + else + { + nano::pending_info info (ii->second); + if (info.amount.number () >= threshold.number ()) + { + if (source || min_version) + { + boost::property_tree::ptree pending_tree; + pending_tree.put ("amount", info.amount.number ().convert_to ()); + if (source) + { + pending_tree.put ("source", info.source.to_account ()); + } + if (min_version) + { + pending_tree.put ("min_version", info.epoch == nano::epoch::epoch_1 ? "1" : "0"); + } + peers_l.add_child (key.hash.to_string (), pending_tree); + } + else + { + peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); + } + } + } + } + } + if (!peers_l.empty ()) + { + pending.add_child (account.to_account (), peers_l); + } + } + response_l.add_child ("blocks", pending); + } + response_errors (); +} + +void nano::json_handler::wallet_representative () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + response_l.put ("representative", wallet->store.representative (transaction).to_account ()); + } + response_errors (); +} + +void nano::json_handler::wallet_representative_set () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + std::string representative_text (request.get ("representative")); + nano::account representative; + if (!representative.decode_account (representative_text)) + { + bool update_existing_accounts (request.get ("update_existing_accounts", false)); + { + auto transaction (node.wallets.tx_begin_write ()); + if (wallet->store.valid_password (transaction) || !update_existing_accounts) + { + wallet->store.representative_set (transaction, representative); + response_l.put ("set", "1"); + } + else + { + ec = nano::error_common::wallet_locked; + } + } + // Change representative for all wallet accounts + if (!ec && update_existing_accounts) + { + std::vector accounts; + { + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) + { + nano::account account (i->first); + nano::account_info info; + if (!node.store.account_get (block_transaction, account, info)) + { + auto block (node.store.block_get (block_transaction, info.rep_block)); + assert (block != nullptr); + if (block->representative () != representative) + { + accounts.push_back (account); + } + } + } + } + for (auto & account : accounts) + { + // clang-format off + wallet->change_async (account, representative, [](std::shared_ptr) {}, 0, false); + // clang-format on + } + } + } + else + { + ec = nano::error_rpc::bad_representative_number; + } + } + response_errors (); +} + +void nano::json_handler::wallet_republish () +{ + auto wallet (wallet_impl ()); + auto count (count_impl ()); + if (!ec) + { + boost::property_tree::ptree blocks; + std::deque> republish_bundle; + auto transaction (node.wallets.tx_begin_read ()); + auto block_transaction (node.store.tx_begin_read ()); + for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) + { + nano::account account (i->first); + auto latest (node.ledger.latest (block_transaction, account)); + std::shared_ptr block; + std::vector hashes; + while (!latest.is_zero () && hashes.size () < count) + { + hashes.push_back (latest); + block = node.store.block_get (block_transaction, latest); + latest = block->previous (); + } + std::reverse (hashes.begin (), hashes.end ()); + for (auto & hash : hashes) + { + block = node.store.block_get (block_transaction, hash); + republish_bundle.push_back (std::move (block)); + boost::property_tree::ptree entry; + entry.put ("", hash.to_string ()); + blocks.push_back (std::make_pair ("", entry)); + } + } + node.network.flood_block_batch (republish_bundle, 25); + response_l.add_child ("blocks", blocks); + } + response_errors (); +} + +void nano::json_handler::wallet_seed () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + if (wallet->store.valid_password (transaction)) + { + nano::raw_key seed; + wallet->store.seed (seed, transaction); + response_l.put ("seed", seed.data.to_string ()); + } + else + { + ec = nano::error_common::wallet_locked; + } + } + response_errors (); +} + +void nano::json_handler::wallet_work_get () +{ + auto wallet (wallet_impl ()); + if (!ec) + { + boost::property_tree::ptree works; + auto transaction (node.wallets.tx_begin_read ()); + for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) + { + nano::account account (i->first); + uint64_t work (0); + auto error_work (wallet->store.work_get (transaction, account, work)); + (void)error_work; + works.put (account.to_account (), nano::to_string_hex (work)); + } + response_l.add_child ("works", works); + } + response_errors (); +} + +void nano::json_handler::work_generate () +{ + auto hash (hash_impl ()); + uint64_t difficulty (node.network_params.network.publish_threshold); + boost::optional difficulty_text (request.get_optional ("difficulty")); + if (!ec && difficulty_text.is_initialized ()) + { + if (nano::from_string_hex (difficulty_text.get (), difficulty)) + { + ec = nano::error_rpc::bad_difficulty_format; + } + } + if (!ec && difficulty > node_rpc_config.max_work_generate_difficulty) + { + ec = nano::error_rpc::difficulty_limit; + } + if (!ec) + { + bool use_peers (request.get_optional ("use_peers") == true); + auto rpc_l (shared_from_this ()); + auto callback = [rpc_l](boost::optional const & work_a) { + if (work_a) + { + boost::property_tree::ptree response_l; + response_l.put ("work", nano::to_string_hex (work_a.value ())); + std::stringstream ostream; + boost::property_tree::write_json (ostream, response_l); + rpc_l->response (ostream.str ()); + } + else + { + json_error_response (rpc_l->response, "Cancelled"); + } + }; + if (!use_peers) + { + node.work.generate (hash, callback, difficulty); + } + else + { + node.work_generate (hash, callback, difficulty); + } + } + // Because of callback + if (ec) + { + response_errors (); + } +} + +void nano::json_handler::work_cancel () +{ + auto hash (hash_impl ()); + if (!ec) + { + node.work.cancel (hash); + } + response_errors (); +} + +void nano::json_handler::work_get () +{ + auto wallet (wallet_impl ()); + auto account (account_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_read ()); + wallet_account_impl (transaction, wallet, account); + if (!ec) + { + uint64_t work (0); + auto error_work (wallet->store.work_get (transaction, account, work)); + (void)error_work; + response_l.put ("work", nano::to_string_hex (work)); + } + } + response_errors (); +} + +void nano::json_handler::work_set () +{ + auto wallet (wallet_impl ()); + auto account (account_impl ()); + auto work (work_optional_impl ()); + if (!ec) + { + auto transaction (node.wallets.tx_begin_write ()); + wallet_account_impl (transaction, wallet, account); + if (!ec) + { + wallet->store.work_put (transaction, account, work); + response_l.put ("success", ""); + } + } + response_errors (); +} + +void nano::json_handler::work_validate () +{ + auto hash (hash_impl ()); + auto work (work_optional_impl ()); + uint64_t difficulty (node.network_params.network.publish_threshold); + boost::optional difficulty_text (request.get_optional ("difficulty")); + if (!ec && difficulty_text.is_initialized ()) + { + if (nano::from_string_hex (difficulty_text.get (), difficulty)) + { + ec = nano::error_rpc::bad_difficulty_format; + } + } + if (!ec) + { + uint64_t result_difficulty (0); + bool invalid (nano::work_validate (hash, work, &result_difficulty)); + bool valid (!invalid && result_difficulty >= difficulty); + response_l.put ("valid", valid ? "1" : "0"); + } + response_errors (); +} + +void nano::json_handler::work_peer_add () +{ + std::string address_text = request.get ("address"); + std::string port_text = request.get ("port"); + uint16_t port; + if (!nano::parse_port (port_text, port)) + { + node.config.work_peers.push_back (std::make_pair (address_text, port)); + response_l.put ("success", ""); + } + else + { + ec = nano::error_common::invalid_port; + } + response_errors (); +} + +void nano::json_handler::work_peers () +{ + boost::property_tree::ptree work_peers_l; + for (auto i (node.config.work_peers.begin ()), n (node.config.work_peers.end ()); i != n; ++i) + { + boost::property_tree::ptree entry; + entry.put ("", boost::str (boost::format ("%1%:%2%") % i->first % i->second)); + work_peers_l.push_back (std::make_pair ("", entry)); + } + response_l.add_child ("work_peers", work_peers_l); + response_errors (); +} + +void nano::json_handler::work_peers_clear () +{ + node.config.work_peers.clear (); + response_l.put ("success", ""); + response_errors (); +} + +namespace +{ +void construct_json (nano::seq_con_info_component * component, boost::property_tree::ptree & parent) +{ + // We are a leaf node, print name and exit + if (!component->is_composite ()) + { + auto & leaf_info = static_cast (component)->get_info (); + boost::property_tree::ptree child; + child.put ("count", leaf_info.count); + child.put ("size", leaf_info.count * leaf_info.sizeof_element); + parent.add_child (leaf_info.name, child); + return; + } + + auto composite = static_cast (component); + + boost::property_tree::ptree current; + for (auto & child : composite->get_children ()) + { + construct_json (child.get (), current); + } + + parent.add_child (composite->get_name (), current); +} + +// Any RPC handlers which require no arguments (excl default arguments) should go here. +// This is to prevent large if/else chains which compilers can have limits for (MSVC for instance has 128). +ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map () +{ + ipc_json_handler_no_arg_func_map no_arg_funcs; + no_arg_funcs.emplace ("account_balance", &nano::json_handler::account_balance); + no_arg_funcs.emplace ("account_block_count", &nano::json_handler::account_block_count); + no_arg_funcs.emplace ("account_count", &nano::json_handler::account_count); + no_arg_funcs.emplace ("account_create", &nano::json_handler::account_create); + no_arg_funcs.emplace ("account_get", &nano::json_handler::account_get); + no_arg_funcs.emplace ("account_history", &nano::json_handler::account_history); + no_arg_funcs.emplace ("account_info", &nano::json_handler::account_info); + no_arg_funcs.emplace ("account_key", &nano::json_handler::account_key); + no_arg_funcs.emplace ("account_list", &nano::json_handler::account_list); + no_arg_funcs.emplace ("account_move", &nano::json_handler::account_move); + no_arg_funcs.emplace ("account_remove", &nano::json_handler::account_remove); + no_arg_funcs.emplace ("account_representative", &nano::json_handler::account_representative); + no_arg_funcs.emplace ("account_representative_set", &nano::json_handler::account_representative_set); + no_arg_funcs.emplace ("account_weight", &nano::json_handler::account_weight); + no_arg_funcs.emplace ("accounts_balances", &nano::json_handler::accounts_balances); + no_arg_funcs.emplace ("accounts_create", &nano::json_handler::accounts_create); + no_arg_funcs.emplace ("accounts_frontiers", &nano::json_handler::accounts_frontiers); + no_arg_funcs.emplace ("accounts_pending", &nano::json_handler::accounts_pending); + no_arg_funcs.emplace ("active_difficulty", &nano::json_handler::active_difficulty); + no_arg_funcs.emplace ("available_supply", &nano::json_handler::available_supply); + no_arg_funcs.emplace ("block_info", &nano::json_handler::block_info); + no_arg_funcs.emplace ("block", &nano::json_handler::block_info); + no_arg_funcs.emplace ("block_confirm", &nano::json_handler::block_confirm); + no_arg_funcs.emplace ("blocks", &nano::json_handler::blocks); + no_arg_funcs.emplace ("blocks_info", &nano::json_handler::blocks_info); + no_arg_funcs.emplace ("block_account", &nano::json_handler::block_account); + no_arg_funcs.emplace ("block_count", &nano::json_handler::block_count); + no_arg_funcs.emplace ("block_count_type", &nano::json_handler::block_count_type); + no_arg_funcs.emplace ("block_create", &nano::json_handler::block_create); + no_arg_funcs.emplace ("block_hash", &nano::json_handler::block_hash); + no_arg_funcs.emplace ("bootstrap", &nano::json_handler::bootstrap); + no_arg_funcs.emplace ("bootstrap_any", &nano::json_handler::bootstrap_any); + no_arg_funcs.emplace ("bootstrap_lazy", &nano::json_handler::bootstrap_lazy); + no_arg_funcs.emplace ("bootstrap_status", &nano::json_handler::bootstrap_status); + no_arg_funcs.emplace ("delegators", &nano::json_handler::delegators); + no_arg_funcs.emplace ("delegators_count", &nano::json_handler::delegators_count); + no_arg_funcs.emplace ("deterministic_key", &nano::json_handler::deterministic_key); + no_arg_funcs.emplace ("confirmation_active", &nano::json_handler::confirmation_active); + no_arg_funcs.emplace ("confirmation_history", &nano::json_handler::confirmation_history); + no_arg_funcs.emplace ("confirmation_info", &nano::json_handler::confirmation_info); + no_arg_funcs.emplace ("confirmation_quorum", &nano::json_handler::confirmation_quorum); + no_arg_funcs.emplace ("frontiers", &nano::json_handler::frontiers); + no_arg_funcs.emplace ("frontier_count", &nano::json_handler::account_count); + no_arg_funcs.emplace ("keepalive", &nano::json_handler::keepalive); + no_arg_funcs.emplace ("key_create", &nano::json_handler::key_create); + no_arg_funcs.emplace ("key_expand", &nano::json_handler::key_expand); + no_arg_funcs.emplace ("ledger", &nano::json_handler::ledger); + no_arg_funcs.emplace ("node_id", &nano::json_handler::node_id); + no_arg_funcs.emplace ("node_id_delete", &nano::json_handler::node_id_delete); + no_arg_funcs.emplace ("password_change", &nano::json_handler::password_change); + no_arg_funcs.emplace ("password_enter", &nano::json_handler::password_enter); + no_arg_funcs.emplace ("wallet_unlock", &nano::json_handler::password_enter); + no_arg_funcs.emplace ("payment_begin", &nano::json_handler::payment_begin); + no_arg_funcs.emplace ("payment_init", &nano::json_handler::payment_init); + no_arg_funcs.emplace ("payment_end", &nano::json_handler::payment_end); + no_arg_funcs.emplace ("payment_wait", &nano::json_handler::payment_wait); + no_arg_funcs.emplace ("peers", &nano::json_handler::peers); + no_arg_funcs.emplace ("pending", &nano::json_handler::pending); + no_arg_funcs.emplace ("pending_exists", &nano::json_handler::pending_exists); + no_arg_funcs.emplace ("process", &nano::json_handler::process); + no_arg_funcs.emplace ("receive", &nano::json_handler::receive); + no_arg_funcs.emplace ("receive_minimum", &nano::json_handler::receive_minimum); + no_arg_funcs.emplace ("receive_minimum_set", &nano::json_handler::receive_minimum_set); + no_arg_funcs.emplace ("representatives", &nano::json_handler::representatives); + no_arg_funcs.emplace ("representatives_online", &nano::json_handler::representatives_online); + no_arg_funcs.emplace ("republish", &nano::json_handler::republish); + no_arg_funcs.emplace ("search_pending", &nano::json_handler::search_pending); + no_arg_funcs.emplace ("search_pending_all", &nano::json_handler::search_pending_all); + no_arg_funcs.emplace ("send", &nano::json_handler::send); + no_arg_funcs.emplace ("sign", &nano::json_handler::sign); + no_arg_funcs.emplace ("stats", &nano::json_handler::stats); + no_arg_funcs.emplace ("stats_clear", &nano::json_handler::stats_clear); + no_arg_funcs.emplace ("stop", &nano::json_handler::stop); + no_arg_funcs.emplace ("unchecked", &nano::json_handler::unchecked); + no_arg_funcs.emplace ("unchecked_clear", &nano::json_handler::unchecked_clear); + no_arg_funcs.emplace ("unchecked_get", &nano::json_handler::unchecked_get); + no_arg_funcs.emplace ("unchecked_keys", &nano::json_handler::unchecked_keys); + no_arg_funcs.emplace ("unopened", &nano::json_handler::unopened); + no_arg_funcs.emplace ("uptime", &nano::json_handler::uptime); + no_arg_funcs.emplace ("validate_account_number", &nano::json_handler::validate_account_number); + no_arg_funcs.emplace ("version", &nano::json_handler::version); + no_arg_funcs.emplace ("wallet_add", &nano::json_handler::wallet_add); + no_arg_funcs.emplace ("wallet_add_watch", &nano::json_handler::wallet_add_watch); + no_arg_funcs.emplace ("wallet_balances", &nano::json_handler::wallet_balances); + no_arg_funcs.emplace ("wallet_change_seed", &nano::json_handler::wallet_change_seed); + no_arg_funcs.emplace ("wallet_contains", &nano::json_handler::wallet_contains); + no_arg_funcs.emplace ("wallet_create", &nano::json_handler::wallet_create); + no_arg_funcs.emplace ("wallet_destroy", &nano::json_handler::wallet_destroy); + no_arg_funcs.emplace ("wallet_export", &nano::json_handler::wallet_export); + no_arg_funcs.emplace ("wallet_frontiers", &nano::json_handler::wallet_frontiers); + no_arg_funcs.emplace ("wallet_history", &nano::json_handler::wallet_history); + no_arg_funcs.emplace ("wallet_info", &nano::json_handler::wallet_info); + no_arg_funcs.emplace ("wallet_balance_total", &nano::json_handler::wallet_info); + no_arg_funcs.emplace ("wallet_key_valid", &nano::json_handler::wallet_key_valid); + no_arg_funcs.emplace ("wallet_ledger", &nano::json_handler::wallet_ledger); + no_arg_funcs.emplace ("wallet_lock", &nano::json_handler::wallet_lock); + no_arg_funcs.emplace ("wallet_pending", &nano::json_handler::wallet_pending); + no_arg_funcs.emplace ("wallet_representative", &nano::json_handler::wallet_representative); + no_arg_funcs.emplace ("wallet_representative_set", &nano::json_handler::wallet_representative_set); + no_arg_funcs.emplace ("wallet_republish", &nano::json_handler::wallet_republish); + no_arg_funcs.emplace ("wallet_work_get", &nano::json_handler::wallet_work_get); + no_arg_funcs.emplace ("work_generate", &nano::json_handler::work_generate); + no_arg_funcs.emplace ("work_cancel", &nano::json_handler::work_cancel); + no_arg_funcs.emplace ("work_get", &nano::json_handler::work_get); + no_arg_funcs.emplace ("work_set", &nano::json_handler::work_set); + no_arg_funcs.emplace ("work_validate", &nano::json_handler::work_validate); + no_arg_funcs.emplace ("work_peer_add", &nano::json_handler::work_peer_add); + no_arg_funcs.emplace ("work_peers", &nano::json_handler::work_peers); + no_arg_funcs.emplace ("work_peers_clear", &nano::json_handler::work_peers_clear); + return no_arg_funcs; +} +} diff --git a/nano/node/json_handler.hpp b/nano/node/json_handler.hpp new file mode 100644 index 00000000..ba913a53 --- /dev/null +++ b/nano/node/json_handler.hpp @@ -0,0 +1,195 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace nano +{ +class node; +class node_rpc_config; + +class json_handler : public std::enable_shared_from_this +{ +public: + json_handler (nano::node &, nano::node_rpc_config const &, std::string const &, std::function const &, std::function 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 response; + void response_errors (); + std::error_code ec; + std::string action; + boost::property_tree::ptree response_l; + std::shared_ptr wallet_impl (); + bool wallet_locked_impl (nano::transaction const &, std::shared_ptr); + bool wallet_account_impl (nano::transaction const &, std::shared_ptr, nano::account const &); + nano::account account_impl (std::string = ""); + nano::amount amount_impl (); + std::shared_ptr block_impl (bool = true); + std::shared_ptr 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::max ()); + uint64_t offset_optional_impl (uint64_t = 0); + bool enable_sign_hash{ false }; + std::function 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 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 response_a) override + { + // Note that if the rpc action is async, the shared_ptr lifetime will be extended by the action handler + auto handler (std::make_shared (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 rpc; + std::function stop_callback; + nano::node_rpc_config const & node_rpc_config; +}; +} diff --git a/nano/node/json_payment_observer.cpp b/nano/node/json_payment_observer.cpp new file mode 100644 index 00000000..929aee89 --- /dev/null +++ b/nano/node/json_payment_observer.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include + +nano::json_payment_observer::json_payment_observer (nano::node & node_a, std::function 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 (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); + } +} diff --git a/nano/node/json_payment_observer.hpp b/nano/node/json_payment_observer.hpp new file mode 100644 index 00000000..184e3d01 --- /dev/null +++ b/nano/node/json_payment_observer.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +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 +{ +public: + json_payment_observer (nano::node &, std::function 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 response; + std::atomic_flag completed; +}; +} diff --git a/nano/node/logging.cpp b/nano/node/logging.cpp index 7b153720..2280825b 100644 --- a/nano/node/logging.cpp +++ b/nano/node/logging.cpp @@ -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 ("network_node_id_handshake", network_node_id_handshake_logging_value); json.get ("node_lifetime_tracing", node_lifetime_tracing_value); json.get ("insufficient_work", insufficient_work_logging_value); - json.get ("log_rpc", log_rpc_value); json.get ("log_ipc", log_ipc_value); json.get ("bulk_pull", bulk_pull_logging_value); json.get ("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; diff --git a/nano/node/logging.hpp b/nano/node/logging.hpp index ef042964..413723fe 100644 --- a/nano/node/logging.hpp +++ b/nano/node/logging.hpp @@ -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 }; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index b269eb44..0eca2b5f 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -950,18 +950,6 @@ void nano::vote_processor::calculate_weights () namespace nano { -std::unique_ptr collect_seq_con_info (node_observers & node_observers, const std::string & name) -{ - auto composite = std::make_unique (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 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) diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 23521de3..22dc110e 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -371,18 +373,6 @@ public: bool wallets_store_init{ false }; bool wallet_init{ false }; }; -class node_observers final -{ -public: - nano::observer_set, nano::account const &, nano::uint128_t const &, bool> blocks; - nano::observer_set wallet; - nano::observer_set, std::shared_ptr> vote; - nano::observer_set account_balance; - nano::observer_set> endpoint; - nano::observer_set<> disconnect; -}; - -std::unique_ptr 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; diff --git a/nano/node/node_observers.cpp b/nano/node/node_observers.cpp new file mode 100644 index 00000000..76a8a96a --- /dev/null +++ b/nano/node/node_observers.cpp @@ -0,0 +1,13 @@ +#include + +std::unique_ptr nano::collect_seq_con_info (nano::node_observers & node_observers, const std::string & name) +{ + auto composite = std::make_unique (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; +} diff --git a/nano/node/node_observers.hpp b/nano/node/node_observers.hpp new file mode 100644 index 00000000..413158bc --- /dev/null +++ b/nano/node/node_observers.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace nano +{ +class node_observers final +{ +public: + using blocks_t = nano::observer_set, nano::account const &, nano::uint128_t const &, bool>; + blocks_t blocks; + nano::observer_set wallet; + nano::observer_set, std::shared_ptr> vote; + nano::observer_set account_balance; + nano::observer_set> endpoint; + nano::observer_set<> disconnect; +}; + +std::unique_ptr collect_seq_con_info (node_observers & node_observers, const std::string & name); +} diff --git a/nano/node/node_rpc_config.cpp b/nano/node/node_rpc_config.cpp new file mode 100644 index 00000000..82dff55b --- /dev/null +++ b/nano/node/node_rpc_config.cpp @@ -0,0 +1,78 @@ +#include + +#include +#include +#include +#include + +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 ("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 ("rpc_in_process"); + if (!rpc_in_process_l) + { + json.put ("rpc_in_process", true); + } + + upgraded_a = true; + } + + json.get_optional ("enable_sign_hash", enable_sign_hash); + std::string max_work_generate_difficulty_text; + json.get_optional ("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 ("rpc_path", rpc_path); + json.get_optional ("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 (rpc_config_path)); + if (rpc_error || rpc_json.empty ()) + { + // Migrate RPC info across + json.write (rpc_config_path); + } +} diff --git a/nano/node/node_rpc_config.hpp b/nano/node/node_rpc_config.hpp new file mode 100644 index 00000000..1368a392 --- /dev/null +++ b/nano/node/node_rpc_config.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +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); +}; +} diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index ef15c47d..ee3d2b20 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -1,5 +1,7 @@ #include +#include #include +#include #include // 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 diff --git a/nano/node/openclconfig.cpp b/nano/node/openclconfig.cpp new file mode 100644 index 00000000..bb88254e --- /dev/null +++ b/nano/node/openclconfig.cpp @@ -0,0 +1,24 @@ +#include + +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 ("platform", platform); + json.get_optional ("device", device); + json.get_optional ("threads", threads); + return json.get_error (); +} diff --git a/nano/node/openclconfig.hpp b/nano/node/openclconfig.hpp new file mode 100644 index 00000000..12715870 --- /dev/null +++ b/nano/node/openclconfig.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +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 }; +}; +} diff --git a/nano/node/openclwork.cpp b/nano/node/openclwork.cpp index 18baa4de..f708c362 100644 --- a/nano/node/openclwork.cpp +++ b/nano/node/openclwork.cpp @@ -1,8 +1,8 @@ -#include - #include #include #include +#include +#include #include #include @@ -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 ("platform", platform); - json.get_optional ("device", device); - json.get_optional ("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), diff --git a/nano/node/openclwork.hpp b/nano/node/openclwork.hpp index 697c4232..4782b48d 100644 --- a/nano/node/openclwork.hpp +++ b/nano/node/openclwork.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -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: diff --git a/nano/node/payment_observer_processor.cpp b/nano/node/payment_observer_processor.cpp new file mode 100644 index 00000000..4a5f41f4 --- /dev/null +++ b/nano/node/payment_observer_processor.cpp @@ -0,0 +1,41 @@ +#include + +#include + +nano::payment_observer_processor::payment_observer_processor (nano::node_observers::blocks_t & blocks) +{ + blocks.add ([this](std::shared_ptr 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 observer; + { + std::lock_guard 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 payment_observer_a) +{ + std::lock_guard 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 lock (mutex); + assert (payment_observers.find (account_a) != payment_observers.end ()); + payment_observers.erase (account_a); +} diff --git a/nano/node/payment_observer_processor.hpp b/nano/node/payment_observer_processor.hpp new file mode 100644 index 00000000..513dec09 --- /dev/null +++ b/nano/node/payment_observer_processor.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +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 payment_observer_a); + void erase (nano::account & account_a); + +private: + std::mutex mutex; + std::unordered_map> payment_observers; +}; +} diff --git a/nano/node/portmapping.hpp b/nano/node/portmapping.hpp index 32701393..d492415a 100644 --- a/nano/node/portmapping.hpp +++ b/nano/node/portmapping.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include diff --git a/nano/node/testing.cpp b/nano/node/testing.cpp index 5cb969bc..2ecc4bb2 100644 --- a/nano/node/testing.cpp +++ b/nano/node/testing.cpp @@ -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 (); + } +} +} diff --git a/nano/node/transport/transport.hpp b/nano/node/transport/transport.hpp index 90be5c5b..6a812d00 100644 --- a/nano/node/transport/transport.hpp +++ b/nano/node/transport/transport.hpp @@ -2,6 +2,7 @@ #include +#include #include namespace nano diff --git a/nano/qt_test/entry.cpp b/nano/qt_test/entry.cpp index 3fcbf587..28068213 100644 --- a/nano/qt_test/entry.cpp +++ b/nano/qt_test/entry.cpp @@ -1,9 +1,9 @@ #include #include -#include 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; } diff --git a/nano/rpc/CMakeLists.txt b/nano/rpc/CMakeLists.txt index 404e0b72..77890140 100644 --- a/nano/rpc/CMakeLists.txt +++ b/nano/rpc/CMakeLists.txt @@ -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}) \ No newline at end of file diff --git a/nano/rpc/rpc.cpp b/nano/rpc/rpc.cpp index 76d7cad7..88d66abc 100644 --- a/nano/rpc/rpc.cpp +++ b/nano/rpc/rpc.cpp @@ -1,62 +1,50 @@ -#include -#include -#include -#include - +#include +#include #include #include -#include #ifdef NANO_SECURE_RPC #include #endif -#include - -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 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 (node, *this)); + auto connection (std::make_shared (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 observer; - { - std::lock_guard 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 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 (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 lock (rpc.mutex); - assert (rpc.payment_observers.find (account) != rpc.payment_observers.end ()); - rpc.payment_observers.erase (account); - } -} - -std::unique_ptr nano::get_rpc (boost::asio::io_context & io_ctx_a, nano::node & node_a, nano::rpc_config const & config_a) +std::unique_ptr 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 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 (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 (io_ctx_a, config_a, rpc_handler_interface_a); } return impl; diff --git a/nano/rpc/rpc.hpp b/nano/rpc/rpc.hpp index ab1ec694..a0f248ac 100644 --- a/nano/rpc/rpc.hpp +++ b/nano/rpc/rpc.hpp @@ -1,74 +1,32 @@ #pragma once -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include 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> payment_observers; - nano::rpc_config config; - nano::node & node; - bool on; -}; -class payment_observer : public std::enable_shared_from_this -{ -public: - payment_observer (std::function 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 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 get_rpc (boost::asio::io_context & io_ctx_a, nano::node & node_a, nano::rpc_config const & config_a); +std::unique_ptr get_rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a); } diff --git a/nano/rpc/rpc_connection.cpp b/nano/rpc/rpc_connection.cpp index f9bae588..3b72b4ed 100644 --- a/nano/rpc/rpc_connection.cpp +++ b/nano/rpc/rpc_connection.cpp @@ -1,14 +1,20 @@ #include #include #include -#include +#include +#include +#include +#include #include #include -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> ()); std::promise header_available_promise; std::future 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 (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::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::steady_clock::now () - start).count () % request_id)); }); auto method = req.method (); switch (method) { case boost::beast::http::verb::post: { - auto handler (std::make_shared (*this_l->node, this_l->rpc, req.body (), request_id, response_handler)); + auto handler (std::make_shared (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 rpc_connection) { // Intentional no-op -} \ No newline at end of file +} diff --git a/nano/rpc/rpc_connection.hpp b/nano/rpc/rpc_connection.hpp index 557f4651..536f85bf 100644 --- a/nano/rpc/rpc_connection.hpp +++ b/nano/rpc/rpc_connection.hpp @@ -2,26 +2,34 @@ #include #include -#include +#include 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 { 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 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 node; - nano::rpc & rpc; + boost::asio::ip::tcp::socket socket; boost::beast::flat_buffer buffer; boost::beast::http::response 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; }; } diff --git a/nano/rpc/rpc_connection_secure.cpp b/nano/rpc/rpc_connection_secure.cpp index dc823e95..51a99eca 100644 --- a/nano/rpc/rpc_connection_secure.cpp +++ b/nano/rpc/rpc_connection_secure.cpp @@ -3,9 +3,9 @@ #include -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 ()); } } diff --git a/nano/rpc/rpc_connection_secure.hpp b/nano/rpc/rpc_connection_secure.hpp index 578f97c1..439687a7 100644 --- a/nano/rpc/rpc_connection_secure.hpp +++ b/nano/rpc/rpc_connection_secure.hpp @@ -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 rpc) override; /** The TLS handshake callback */ diff --git a/nano/rpc/rpc_handler.cpp b/nano/rpc/rpc_handler.cpp index 899fe383..afe7af2a 100644 --- a/nano/rpc/rpc_handler.cpp +++ b/nano/rpc/rpc_handler.cpp @@ -1,4383 +1,155 @@ #include #include #include -#include +#include +#include +#include #include -#include -#include #include #include namespace { -void construct_json (nano::seq_con_info_component * component, boost::property_tree::ptree & parent); -using rpc_handler_no_arg_func_map = std::unordered_map>; -rpc_handler_no_arg_func_map create_rpc_handler_no_arg_func_map (); -auto rpc_handler_no_arg_funcs = create_rpc_handler_no_arg_func_map (); +std::unordered_set create_rpc_control_impls (); +std::unordered_set rpc_control_impl_set = create_rpc_control_impls (); +std::string filter_request (boost::property_tree::ptree tree_a); } -nano::rpc_handler::rpc_handler (nano::node & node_a, nano::rpc & rpc_a, std::string const & body_a, std::string const & request_id_a, std::function const & response_a) : +nano::rpc_handler::rpc_handler (nano::rpc_config const & rpc_config, std::string const & body_a, std::string const & request_id_a, std::function const & response_a, nano::rpc_handler_interface & rpc_handler_interface_a, nano::logger_mt & logger) : body (body_a), request_id (request_id_a), -node (node_a), -rpc (rpc_a), -response (response_a) +response (response_a), +rpc_config (rpc_config), +rpc_handler_interface (rpc_handler_interface_a), +logger (logger) { } -void nano::rpc_handler::response_errors () +void nano::rpc_handler::process_request () { - if (ec || response_l.empty ()) - { - boost::property_tree::ptree response_error; - response_error.put ("error", ec ? ec.message () : "Empty response"); - response (response_error); - } - else - { - response (response_l); - } -} - -std::shared_ptr nano::rpc_handler::wallet_impl () -{ - if (!ec) - { - std::string wallet_text (request.get ("wallet")); - nano::uint256_union wallet; - if (!wallet.decode_hex (wallet_text)) - { - auto existing (node.wallets.items.find (wallet)); - if (existing != node.wallets.items.end ()) - { - return existing->second; - } - else - { - ec = nano::error_common::wallet_not_found; - } - } - else - { - ec = nano::error_common::bad_wallet_number; - } - } - return nullptr; -} - -bool nano::rpc_handler::wallet_locked_impl (nano::transaction const & transaction_a, std::shared_ptr wallet_a) -{ - bool result (false); - if (!ec) - { - if (!wallet_a->store.valid_password (transaction_a)) - { - ec = nano::error_common::wallet_locked; - result = true; - } - } - return result; -} - -bool nano::rpc_handler::wallet_account_impl (nano::transaction const & transaction_a, std::shared_ptr wallet_a, nano::account const & account_a) -{ - bool result (false); - if (!ec) - { - if (wallet_a->store.find (transaction_a, account_a) != wallet_a->store.end ()) - { - result = true; - } - else - { - ec = nano::error_common::account_not_found_wallet; - } - } - return result; -} - -nano::account nano::rpc_handler::account_impl (std::string account_text) -{ - nano::account result (0); - if (!ec) - { - if (account_text.empty ()) - { - account_text = request.get ("account"); - } - if (result.decode_account (account_text)) - { - ec = nano::error_common::bad_account_number; - } - } - return result; -} - -nano::amount nano::rpc_handler::amount_impl () -{ - nano::amount result (0); - if (!ec) - { - std::string amount_text (request.get ("amount")); - if (result.decode_dec (amount_text)) - { - ec = nano::error_common::invalid_amount; - } - } - return result; -} - -std::shared_ptr nano::rpc_handler::block_impl (bool signature_work_required) -{ - std::shared_ptr result; - if (!ec) - { - std::string block_text (request.get ("block")); - boost::property_tree::ptree block_l; - std::stringstream block_stream (block_text); - boost::property_tree::read_json (block_stream, block_l); - if (!signature_work_required) - { - block_l.put ("signature", "0"); - block_l.put ("work", "0"); - } - result = nano::deserialize_block_json (block_l); - if (result == nullptr) - { - ec = nano::error_blocks::invalid_block; - } - } - return result; -} - -std::shared_ptr nano::rpc_handler::block_json_impl (bool signature_work_required) -{ - std::shared_ptr result; - if (!ec) - { - auto block_l (request.get_child ("block")); - if (!signature_work_required) - { - block_l.put ("signature", "0"); - block_l.put ("work", "0"); - } - result = nano::deserialize_block_json (block_l); - if (result == nullptr) - { - ec = nano::error_blocks::invalid_block; - } - } - return result; -} - -nano::block_hash nano::rpc_handler::hash_impl (std::string search_text) -{ - nano::block_hash result (0); - if (!ec) - { - std::string hash_text (request.get (search_text)); - if (result.decode_hex (hash_text)) - { - ec = nano::error_blocks::invalid_block_hash; - } - } - return result; -} - -nano::amount nano::rpc_handler::threshold_optional_impl () -{ - nano::amount result (0); - boost::optional threshold_text (request.get_optional ("threshold")); - if (!ec && threshold_text.is_initialized ()) - { - if (result.decode_dec (threshold_text.get ())) - { - ec = nano::error_common::bad_threshold; - } - } - return result; -} - -uint64_t nano::rpc_handler::work_optional_impl () -{ - uint64_t result (0); - boost::optional work_text (request.get_optional ("work")); - if (!ec && work_text.is_initialized ()) - { - if (nano::from_string_hex (work_text.get (), result)) - { - ec = nano::error_common::bad_work_format; - } - } - return result; -} - -namespace -{ -bool decode_unsigned (std::string const & text, uint64_t & number) -{ - bool result; - size_t end; try { - number = std::stoull (text, &end); - result = false; - } - catch (std::invalid_argument const &) - { - result = true; - } - catch (std::out_of_range const &) - { - result = true; - } - result = result || end != text.size (); - return result; -} -} - -uint64_t nano::rpc_handler::count_impl () -{ - uint64_t result (0); - if (!ec) - { - std::string count_text (request.get ("count")); - if (decode_unsigned (count_text, result) || result == 0) + auto max_depth_exceeded (false); + auto max_depth_possible (0u); + for (auto ch : body) { - ec = nano::error_common::invalid_count; - } - } - return result; -} - -uint64_t nano::rpc_handler::count_optional_impl (uint64_t result) -{ - boost::optional count_text (request.get_optional ("count")); - if (!ec && count_text.is_initialized ()) - { - if (decode_unsigned (count_text.get (), result)) - { - ec = nano::error_common::invalid_count; - } - } - return result; -} - -uint64_t nano::rpc_handler::offset_optional_impl (uint64_t result) -{ - boost::optional offset_text (request.get_optional ("offset")); - if (!ec && offset_text.is_initialized ()) - { - if (decode_unsigned (offset_text.get (), result)) - { - ec = nano::error_rpc::invalid_offset; - } - } - return result; -} - -bool nano::rpc_handler::rpc_control_impl () -{ - bool result (false); - if (!ec) - { - if (!rpc.config.enable_control) - { - ec = nano::error_rpc::rpc_control_disabled; - } - else - { - result = true; - } - } - return result; -} - -void nano::rpc_handler::account_balance () -{ - auto account (account_impl ()); - if (!ec) - { - auto balance (node.balance_pending (account)); - response_l.put ("balance", balance.first.convert_to ()); - response_l.put ("pending", balance.second.convert_to ()); - } - response_errors (); -} - -void nano::rpc_handler::account_block_count () -{ - auto account (account_impl ()); - if (!ec) - { - auto transaction (node.store.tx_begin_read ()); - nano::account_info info; - if (!node.store.account_get (transaction, account, info)) - { - response_l.put ("block_count", std::to_string (info.block_count)); - } - else - { - ec = nano::error_common::account_not_found; - } - } - response_errors (); -} - -void nano::rpc_handler::account_create () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - const bool generate_work = request.get ("work", true); - nano::account new_key; - auto index_text (request.get_optional ("index")); - if (index_text.is_initialized ()) - { - uint64_t index; - if (decode_unsigned (index_text.get (), index) || index > static_cast (std::numeric_limits::max ())) + if (ch == '[' || ch == '{') { - ec = nano::error_common::invalid_index; + if (max_depth_possible >= rpc_config.max_json_depth) + { + max_depth_exceeded = true; + break; + } + ++max_depth_possible; + } + } + if (max_depth_exceeded) + { + json_error_response (response, "Max JSON depth exceeded"); + } + else + { + std::stringstream ss; + ss << body; + boost::property_tree::ptree request; + boost::property_tree::read_json (ss, request); + + auto action = request.get ("action"); + logger.always_log (boost::str (boost::format ("%1% ") % request_id), filter_request (request)); + + // Check if this is a RPC command which requires RPC enabled control + std::error_code rpc_control_disabled_ec = nano::error_rpc::rpc_control_disabled; + + bool error = false; + auto found = rpc_control_impl_set.find (action); + if (found != rpc_control_impl_set.cend () && !rpc_config.enable_control) + { + json_error_response (response, rpc_control_disabled_ec.message ()); + error = true; } else { - new_key = wallet->deterministic_insert (static_cast (index), generate_work); - } - } - else - { - new_key = wallet->deterministic_insert (generate_work); - } - - if (!ec) - { - if (!new_key.is_zero ()) - { - response_l.put ("account", new_key.to_account ()); - } - else - { - ec = nano::error_common::wallet_locked; - } - } - } - response_errors (); -} - -void nano::rpc_handler::account_get () -{ - std::string key_text (request.get ("key")); - nano::uint256_union pub; - if (!pub.decode_hex (key_text)) - { - response_l.put ("account", pub.to_account ()); - } - else - { - ec = nano::error_common::bad_public_key; - } - response_errors (); -} - -void nano::rpc_handler::account_info () -{ - auto account (account_impl ()); - if (!ec) - { - const bool representative = request.get ("representative", false); - const bool weight = request.get ("weight", false); - const bool pending = request.get ("pending", false); - auto transaction (node.store.tx_begin_read ()); - nano::account_info info; - if (!node.store.account_get (transaction, account, info)) - { - response_l.put ("frontier", info.head.to_string ()); - response_l.put ("open_block", info.open_block.to_string ()); - response_l.put ("representative_block", info.rep_block.to_string ()); - std::string balance; - nano::uint128_union (info.balance).encode_dec (balance); - response_l.put ("balance", balance); - response_l.put ("modified_timestamp", std::to_string (info.modified)); - response_l.put ("block_count", std::to_string (info.block_count)); - response_l.put ("account_version", info.epoch == nano::epoch::epoch_1 ? "1" : "0"); - response_l.put ("confirmation_height", std::to_string (info.confirmation_height)); - if (representative) - { - auto block (node.store.block_get (transaction, info.rep_block)); - assert (block != nullptr); - response_l.put ("representative", block->representative ().to_account ()); - } - if (weight) - { - auto account_weight (node.ledger.weight (transaction, account)); - response_l.put ("weight", account_weight.convert_to ()); - } - if (pending) - { - auto account_pending (node.ledger.account_pending (transaction, account)); - response_l.put ("pending", account_pending.convert_to ()); - } - } - else - { - ec = nano::error_common::account_not_found; - } - } - response_errors (); -} - -void nano::rpc_handler::account_key () -{ - auto account (account_impl ()); - if (!ec) - { - response_l.put ("key", account.to_string ()); - } - response_errors (); -} - -void nano::rpc_handler::account_list () -{ - auto wallet (wallet_impl ()); - if (!ec) - { - boost::property_tree::ptree accounts; - auto transaction (node.wallets.tx_begin_read ()); - for (auto i (wallet->store.begin (transaction)), j (wallet->store.end ()); i != j; ++i) - { - boost::property_tree::ptree entry; - entry.put ("", nano::account (i->first).to_account ()); - accounts.push_back (std::make_pair ("", entry)); - } - response_l.add_child ("accounts", accounts); - } - response_errors (); -} - -void nano::rpc_handler::account_move () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - std::string source_text (request.get ("source")); - auto accounts_text (request.get_child ("accounts")); - nano::uint256_union source; - if (!source.decode_hex (source_text)) - { - auto existing (node.wallets.items.find (source)); - if (existing != node.wallets.items.end ()) - { - auto source (existing->second); - std::vector accounts; - for (auto i (accounts_text.begin ()), n (accounts_text.end ()); i != n; ++i) + // Special case with stats, type -> objects + if (action == "stats" && !rpc_config.enable_control) { - nano::public_key account; - account.decode_account (i->second.get ("")); - accounts.push_back (account); - } - auto transaction (node.wallets.tx_begin_write ()); - auto error (wallet->store.move (transaction, source->store, accounts)); - response_l.put ("moved", error ? "0" : "1"); - } - else - { - ec = nano::error_rpc::source_not_found; - } - } - else - { - ec = nano::error_rpc::bad_source; - } - } - response_errors (); -} - -void nano::rpc_handler::account_remove () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - auto account (account_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_write ()); - wallet_locked_impl (transaction, wallet); - wallet_account_impl (transaction, wallet, account); - if (!ec) - { - wallet->store.erase (transaction, account); - response_l.put ("removed", "1"); - } - } - response_errors (); -} - -void nano::rpc_handler::account_representative () -{ - auto account (account_impl ()); - if (!ec) - { - auto transaction (node.store.tx_begin_read ()); - nano::account_info info; - if (!node.store.account_get (transaction, account, info)) - { - auto block (node.store.block_get (transaction, info.rep_block)); - assert (block != nullptr); - response_l.put ("representative", block->representative ().to_account ()); - } - else - { - ec = nano::error_common::account_not_found; - } - } - response_errors (); -} - -void nano::rpc_handler::account_representative_set () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - auto account (account_impl ()); - if (!ec) - { - std::string representative_text (request.get ("representative")); - nano::account representative; - if (!representative.decode_account (representative_text)) - { - auto work (work_optional_impl ()); - if (!ec && work) - { - auto transaction (node.wallets.tx_begin_write ()); - wallet_locked_impl (transaction, wallet); - wallet_account_impl (transaction, wallet, account); - if (!ec) - { - nano::account_info info; - auto block_transaction (node.store.tx_begin_read ()); - if (!node.store.account_get (block_transaction, account, info)) + if (request.get ("type") == "objects") { - if (nano::work_validate (info.head, work)) - { - ec = nano::error_common::invalid_work; - } + json_error_response (response, rpc_control_disabled_ec.message ()); + error = true; } - else + } + else if (action == "process") + { + auto force = request.get_optional ("force"); + if (force && !rpc_config.enable_control) { - ec = nano::error_common::account_not_found; + json_error_response (response, rpc_control_disabled_ec.message ()); + error = true; } } } - if (!ec) - { - bool generate_work (work == 0); // Disable work generation if "work" option is provided - auto response_a (response); - // clang-format off - wallet->change_async (account, representative, [response_a](std::shared_ptr block) { - if (block != nullptr) - { - boost::property_tree::ptree response_l; - response_l.put ("block", block->hash ().to_string ()); - response_a (response_l); - } - else - { - error_response (response_a, "Error generating block"); - } - }, - work, generate_work); - // clang-format on - } - } - else - { - ec = nano::error_rpc::bad_representative_number; - } - } - // Because of change_async - if (ec) - { - response_errors (); - } -} -void nano::rpc_handler::account_weight () -{ - auto account (account_impl ()); - if (!ec) - { - auto balance (node.weight (account)); - response_l.put ("weight", balance.convert_to ()); - } - response_errors (); -} - -void nano::rpc_handler::accounts_balances () -{ - boost::property_tree::ptree balances; - for (auto & accounts : request.get_child ("accounts")) - { - auto account (account_impl (accounts.second.data ())); - if (!ec) - { - boost::property_tree::ptree entry; - auto balance (node.balance_pending (account)); - entry.put ("balance", balance.first.convert_to ()); - entry.put ("pending", balance.second.convert_to ()); - balances.push_back (std::make_pair (account.to_account (), entry)); - } - } - response_l.add_child ("balances", balances); - response_errors (); -} - -void nano::rpc_handler::accounts_create () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - auto count (count_impl ()); - if (!ec) - { - const bool generate_work = request.get ("work", false); - boost::property_tree::ptree accounts; - for (auto i (0); accounts.size () < count; ++i) - { - nano::account new_key (wallet->deterministic_insert (generate_work)); - if (!new_key.is_zero ()) + if (!error) { - boost::property_tree::ptree entry; - entry.put ("", new_key.to_account ()); - accounts.push_back (std::make_pair ("", entry)); - } - } - response_l.add_child ("accounts", accounts); - } - response_errors (); -} - -void nano::rpc_handler::accounts_frontiers () -{ - boost::property_tree::ptree frontiers; - auto transaction (node.store.tx_begin_read ()); - for (auto & accounts : request.get_child ("accounts")) - { - auto account (account_impl (accounts.second.data ())); - if (!ec) - { - auto latest (node.ledger.latest (transaction, account)); - if (!latest.is_zero ()) - { - frontiers.put (account.to_account (), latest.to_string ()); + rpc_handler_interface.process_request (action, body, this->response); } } } - response_l.add_child ("frontiers", frontiers); - response_errors (); -} - -void nano::rpc_handler::accounts_pending () -{ - auto count (count_optional_impl ()); - auto threshold (threshold_optional_impl ()); - const bool source = request.get ("source", false); - const bool include_active = request.get ("include_active", false); - const bool sorting = request.get ("sorting", false); - auto simple (threshold.is_zero () && !source && !sorting); // if simple, response is a list of hashes for each account - boost::property_tree::ptree pending; - auto transaction (node.store.tx_begin_read ()); - for (auto & accounts : request.get_child ("accounts")) + catch (std::runtime_error const &) { - auto account (account_impl (accounts.second.data ())); - if (!ec) - { - boost::property_tree::ptree peers_l; - for (auto i (node.store.pending_begin (transaction, nano::pending_key (account, 0))); nano::pending_key (i->first).account == account && peers_l.size () < count; ++i) - { - nano::pending_key key (i->first); - if (include_active || node.ledger.block_confirmed (transaction, key.hash)) - { - if (simple) - { - boost::property_tree::ptree entry; - entry.put ("", key.hash.to_string ()); - peers_l.push_back (std::make_pair ("", entry)); - } - else - { - nano::pending_info info (i->second); - if (info.amount.number () >= threshold.number ()) - { - if (source) - { - boost::property_tree::ptree pending_tree; - pending_tree.put ("amount", info.amount.number ().convert_to ()); - pending_tree.put ("source", info.source.to_account ()); - peers_l.add_child (key.hash.to_string (), pending_tree); - } - else - { - peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); - } - } - } - } - } - if (sorting && !simple) - { - if (source) - { - peers_l.sort ([](const auto & child1, const auto & child2) -> bool { - return child1.second.template get ("amount") > child2.second.template get ("amount"); - }); - } - else - { - peers_l.sort ([](const auto & child1, const auto & child2) -> bool { - return child1.second.template get ("") > child2.second.template get (""); - }); - } - } - pending.add_child (account.to_account (), peers_l); - } + json_error_response (response, "Unable to parse JSON"); } - response_l.add_child ("blocks", pending); - response_errors (); -} - -void nano::rpc_handler::available_supply () -{ - auto genesis_balance (node.balance (node.network_params.ledger.genesis_account)); // Cold storage genesis - auto landing_balance (node.balance (nano::account ("059F68AAB29DE0D3A27443625C7EA9CDDB6517A8B76FE37727EF6A4D76832AD5"))); // Active unavailable account - auto faucet_balance (node.balance (nano::account ("8E319CE6F3025E5B2DF66DA7AB1467FE48F1679C13DD43BFDB29FA2E9FC40D3B"))); // Faucet account - auto burned_balance ((node.balance_pending (nano::account (0))).second); // Burning 0 account - auto available (node.network_params.ledger.genesis_amount - genesis_balance - landing_balance - faucet_balance - burned_balance); - response_l.put ("available", available.convert_to ()); - response_errors (); -} - -void state_subtype (nano::transaction const & transaction_a, nano::node & node_a, std::shared_ptr block_a, nano::uint128_t const & balance_a, boost::property_tree::ptree & tree_a) -{ - // Subtype check - auto previous_balance (node_a.ledger.balance (transaction_a, block_a->previous ())); - if (balance_a < previous_balance) + catch (...) { - tree_a.put ("subtype", "send"); + json_error_response (response, "Internal server error in RPC"); } - else - { - if (block_a->link ().is_zero ()) - { - tree_a.put ("subtype", "change"); - } - else if (balance_a == previous_balance && !node_a.ledger.epoch_link.is_zero () && node_a.ledger.is_epoch_link (block_a->link ())) - { - tree_a.put ("subtype", "epoch"); - } - else - { - tree_a.put ("subtype", "receive"); - } - } -} - -void nano::rpc_handler::block_info () -{ - auto hash (hash_impl ()); - if (!ec) - { - nano::block_sideband sideband; - auto transaction (node.store.tx_begin_read ()); - auto block (node.store.block_get (transaction, hash, &sideband)); - if (block != nullptr) - { - nano::account account (block->account ().is_zero () ? sideband.account : block->account ()); - response_l.put ("block_account", account.to_account ()); - auto amount (node.ledger.amount (transaction, hash)); - response_l.put ("amount", amount.convert_to ()); - auto balance (node.ledger.balance (transaction, hash)); - response_l.put ("balance", balance.convert_to ()); - response_l.put ("height", std::to_string (sideband.height)); - response_l.put ("local_timestamp", std::to_string (sideband.timestamp)); - auto confirmed (node.ledger.block_confirmed (transaction, hash)); - response_l.put ("confirmed", confirmed); - - bool json_block_l = request.get ("json_block", false); - if (json_block_l) - { - boost::property_tree::ptree block_node_l; - block->serialize_json (block_node_l); - response_l.add_child ("contents", block_node_l); - } - else - { - std::string contents; - block->serialize_json (contents); - response_l.put ("contents", contents); - } - if (block->type () == nano::block_type::state) - { - state_subtype (transaction, node, block, balance, response_l); - } - } - else - { - ec = nano::error_blocks::not_found; - } - } - response_errors (); -} - -void nano::rpc_handler::block_confirm () -{ - auto hash (hash_impl ()); - if (!ec) - { - auto transaction (node.store.tx_begin_read ()); - auto block_l (node.store.block_get (transaction, hash)); - if (block_l != nullptr) - { - if (!node.ledger.block_confirmed (transaction, hash)) - { - // Start new confirmation for unconfirmed block - node.block_confirm (std::move (block_l)); - } - else - { - // Add record in confirmation history for confirmed block - nano::election_status status; - status.winner = block_l; - status.tally = 0; - status.election_end = std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()); - status.election_duration = std::chrono::milliseconds::zero (); - { - std::lock_guard lock (node.active.mutex); - node.active.confirmed.push_back (status); - if (node.active.confirmed.size () > node.active.election_history_size) - { - node.active.confirmed.pop_front (); - } - } - // Trigger callback for confirmed block - node.block_arrival.add (hash); - auto account (node.ledger.account (transaction, hash)); - auto amount (node.ledger.amount (transaction, hash)); - bool is_state_send (false); - if (auto state = dynamic_cast (block_l.get ())) - { - is_state_send = node.ledger.is_send (transaction, *state); - } - node.observers.blocks.notify (block_l, account, amount, is_state_send); - } - response_l.put ("started", "1"); - } - else - { - ec = nano::error_blocks::not_found; - } - } - response_errors (); -} - -void nano::rpc_handler::blocks () -{ - const bool json_block_l = request.get ("json_block", false); - std::vector hashes; - boost::property_tree::ptree blocks; - auto transaction (node.store.tx_begin_read ()); - for (boost::property_tree::ptree::value_type & hashes : request.get_child ("hashes")) - { - if (!ec) - { - std::string hash_text = hashes.second.data (); - nano::uint256_union hash; - if (!hash.decode_hex (hash_text)) - { - auto block (node.store.block_get (transaction, hash)); - if (block != nullptr) - { - if (json_block_l) - { - boost::property_tree::ptree block_node_l; - block->serialize_json (block_node_l); - blocks.add_child (hash_text, block_node_l); - } - else - { - std::string contents; - block->serialize_json (contents); - blocks.put (hash_text, contents); - } - } - else - { - ec = nano::error_blocks::not_found; - } - } - else - { - ec = nano::error_blocks::bad_hash_number; - } - } - } - response_l.add_child ("blocks", blocks); - response_errors (); -} - -void nano::rpc_handler::blocks_info () -{ - const bool pending = request.get ("pending", false); - const bool source = request.get ("source", false); - const bool json_block_l = request.get ("json_block", false); - - std::vector hashes; - boost::property_tree::ptree blocks; - auto transaction (node.store.tx_begin_read ()); - for (boost::property_tree::ptree::value_type & hashes : request.get_child ("hashes")) - { - if (!ec) - { - std::string hash_text = hashes.second.data (); - nano::uint256_union hash; - if (!hash.decode_hex (hash_text)) - { - nano::block_sideband sideband; - auto block (node.store.block_get (transaction, hash, &sideband)); - if (block != nullptr) - { - boost::property_tree::ptree entry; - nano::account account (block->account ().is_zero () ? sideband.account : block->account ()); - entry.put ("block_account", account.to_account ()); - auto amount (node.ledger.amount (transaction, hash)); - entry.put ("amount", amount.convert_to ()); - auto balance (node.ledger.balance (transaction, hash)); - entry.put ("balance", balance.convert_to ()); - entry.put ("height", std::to_string (sideband.height)); - entry.put ("local_timestamp", std::to_string (sideband.timestamp)); - auto confirmed (node.ledger.block_confirmed (transaction, hash)); - entry.put ("confirmed", confirmed); - - if (json_block_l) - { - boost::property_tree::ptree block_node_l; - block->serialize_json (block_node_l); - entry.add_child ("contents", block_node_l); - } - else - { - std::string contents; - block->serialize_json (contents); - entry.put ("contents", contents); - } - if (block->type () == nano::block_type::state) - { - state_subtype (transaction, node, block, balance, entry); - } - if (pending) - { - bool exists (false); - auto destination (node.ledger.block_destination (transaction, *block)); - if (!destination.is_zero ()) - { - exists = node.store.pending_exists (transaction, nano::pending_key (destination, hash)); - } - entry.put ("pending", exists ? "1" : "0"); - } - if (source) - { - nano::block_hash source_hash (node.ledger.block_source (transaction, *block)); - auto block_a (node.store.block_get (transaction, source_hash)); - if (block_a != nullptr) - { - auto source_account (node.ledger.account (transaction, source_hash)); - entry.put ("source_account", source_account.to_account ()); - } - else - { - entry.put ("source_account", "0"); - } - } - blocks.push_back (std::make_pair (hash_text, entry)); - } - else - { - ec = nano::error_blocks::not_found; - } - } - else - { - ec = nano::error_blocks::bad_hash_number; - } - } - } - response_l.add_child ("blocks", blocks); - response_errors (); -} - -void nano::rpc_handler::block_account () -{ - auto hash (hash_impl ()); - if (!ec) - { - auto transaction (node.store.tx_begin_read ()); - if (node.store.block_exists (transaction, hash)) - { - auto account (node.ledger.account (transaction, hash)); - response_l.put ("account", account.to_account ()); - } - else - { - ec = nano::error_blocks::not_found; - } - } - response_errors (); -} - -void nano::rpc_handler::block_count () -{ - auto transaction (node.store.tx_begin_read ()); - response_l.put ("count", std::to_string (node.store.block_count (transaction).sum ())); - response_l.put ("unchecked", std::to_string (node.store.unchecked_count (transaction))); - response_errors (); -} - -void nano::rpc_handler::block_count_type () -{ - auto transaction (node.store.tx_begin_read ()); - nano::block_counts count (node.store.block_count (transaction)); - response_l.put ("send", std::to_string (count.send)); - response_l.put ("receive", std::to_string (count.receive)); - response_l.put ("open", std::to_string (count.open)); - response_l.put ("change", std::to_string (count.change)); - response_l.put ("state_v0", std::to_string (count.state_v0)); - response_l.put ("state_v1", std::to_string (count.state_v1)); - response_l.put ("state", std::to_string (count.state_v0 + count.state_v1)); - response_errors (); -} - -void nano::rpc_handler::block_create () -{ - rpc_control_impl (); - if (!ec) - { - std::string type (request.get ("type")); - nano::uint256_union wallet (0); - boost::optional wallet_text (request.get_optional ("wallet")); - if (wallet_text.is_initialized ()) - { - if (wallet.decode_hex (wallet_text.get ())) - { - ec = nano::error_common::bad_wallet_number; - } - } - nano::uint256_union account (0); - boost::optional account_text (request.get_optional ("account")); - if (!ec && account_text.is_initialized ()) - { - if (account.decode_account (account_text.get ())) - { - ec = nano::error_common::bad_account_number; - } - } - nano::uint256_union representative (0); - boost::optional representative_text (request.get_optional ("representative")); - if (!ec && representative_text.is_initialized ()) - { - if (representative.decode_account (representative_text.get ())) - { - ec = nano::error_rpc::bad_representative_number; - } - } - nano::uint256_union destination (0); - boost::optional destination_text (request.get_optional ("destination")); - if (!ec && destination_text.is_initialized ()) - { - if (destination.decode_account (destination_text.get ())) - { - ec = nano::error_rpc::bad_destination; - } - } - nano::block_hash source (0); - boost::optional source_text (request.get_optional ("source")); - if (!ec && source_text.is_initialized ()) - { - if (source.decode_hex (source_text.get ())) - { - ec = nano::error_rpc::bad_source; - } - } - nano::uint128_union amount (0); - boost::optional amount_text (request.get_optional ("amount")); - if (!ec && amount_text.is_initialized ()) - { - if (amount.decode_dec (amount_text.get ())) - { - ec = nano::error_common::invalid_amount; - } - } - auto work (work_optional_impl ()); - nano::raw_key prv; - prv.data.clear (); - nano::uint256_union previous (0); - nano::uint128_union balance (0); - if (!ec && wallet != 0 && account != 0) - { - auto existing (node.wallets.items.find (wallet)); - if (existing != node.wallets.items.end ()) - { - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - wallet_locked_impl (transaction, existing->second); - wallet_account_impl (transaction, existing->second, account); - if (!ec) - { - existing->second->store.fetch (transaction, account, prv); - previous = node.ledger.latest (block_transaction, account); - balance = node.ledger.account_balance (block_transaction, account); - } - } - else - { - ec = nano::error_common::wallet_not_found; - } - } - boost::optional key_text (request.get_optional ("key")); - if (!ec && key_text.is_initialized ()) - { - if (prv.data.decode_hex (key_text.get ())) - { - ec = nano::error_common::bad_private_key; - } - } - boost::optional previous_text (request.get_optional ("previous")); - if (!ec && previous_text.is_initialized ()) - { - if (previous.decode_hex (previous_text.get ())) - { - ec = nano::error_rpc::bad_previous; - } - } - boost::optional balance_text (request.get_optional ("balance")); - if (!ec && balance_text.is_initialized ()) - { - if (balance.decode_dec (balance_text.get ())) - { - ec = nano::error_rpc::invalid_balance; - } - } - nano::uint256_union link (0); - boost::optional link_text (request.get_optional ("link")); - if (!ec && link_text.is_initialized ()) - { - if (link.decode_account (link_text.get ())) - { - if (link.decode_hex (link_text.get ())) - { - ec = nano::error_rpc::bad_link; - } - } - } - else - { - // Retrieve link from source or destination - link = source.is_zero () ? destination : source; - } - if (prv.data != 0) - { - nano::uint256_union pub (nano::pub_key (prv.data)); - // Fetching account balance & previous for send blocks (if aren't given directly) - if (!previous_text.is_initialized () && !balance_text.is_initialized ()) - { - auto transaction (node.store.tx_begin_read ()); - previous = node.ledger.latest (transaction, pub); - balance = node.ledger.account_balance (transaction, pub); - } - // Double check current balance if previous block is specified - else if (previous_text.is_initialized () && balance_text.is_initialized () && type == "send") - { - auto transaction (node.store.tx_begin_read ()); - if (node.store.block_exists (transaction, previous) && node.store.block_balance (transaction, previous) != balance.number ()) - { - ec = nano::error_rpc::block_create_balance_mismatch; - } - } - // Check for incorrect account key - if (!ec && account_text.is_initialized ()) - { - if (account != pub) - { - ec = nano::error_rpc::block_create_public_key_mismatch; - } - } - if (type == "state") - { - if (previous_text.is_initialized () && !representative.is_zero () && (!link.is_zero () || link_text.is_initialized ())) - { - if (work == 0) - { - work = node.work_generate_blocking (previous.is_zero () ? pub : previous); - } - nano::state_block state (pub, previous, representative, balance, link, prv, pub, work); - response_l.put ("hash", state.hash ().to_string ()); - bool json_block_l = request.get ("json_block", false); - if (json_block_l) - { - boost::property_tree::ptree block_node_l; - state.serialize_json (block_node_l); - response_l.add_child ("block", block_node_l); - } - else - { - std::string contents; - state.serialize_json (contents); - response_l.put ("block", contents); - } - } - else - { - ec = nano::error_rpc::block_create_requirements_state; - } - } - else if (type == "open") - { - if (representative != 0 && source != 0) - { - if (work == 0) - { - work = node.work_generate_blocking (pub); - } - nano::open_block open (source, representative, pub, prv, pub, work); - response_l.put ("hash", open.hash ().to_string ()); - std::string contents; - open.serialize_json (contents); - response_l.put ("block", contents); - } - else - { - ec = nano::error_rpc::block_create_requirements_open; - } - } - else if (type == "receive") - { - if (source != 0 && previous != 0) - { - if (work == 0) - { - work = node.work_generate_blocking (previous); - } - nano::receive_block receive (previous, source, prv, pub, work); - response_l.put ("hash", receive.hash ().to_string ()); - std::string contents; - receive.serialize_json (contents); - response_l.put ("block", contents); - } - else - { - ec = nano::error_rpc::block_create_requirements_receive; - } - } - else if (type == "change") - { - if (representative != 0 && previous != 0) - { - if (work == 0) - { - work = node.work_generate_blocking (previous); - } - nano::change_block change (previous, representative, prv, pub, work); - response_l.put ("hash", change.hash ().to_string ()); - std::string contents; - change.serialize_json (contents); - response_l.put ("block", contents); - } - else - { - ec = nano::error_rpc::block_create_requirements_change; - } - } - else if (type == "send") - { - if (destination != 0 && previous != 0 && balance != 0 && amount != 0) - { - if (balance.number () >= amount.number ()) - { - if (work == 0) - { - work = node.work_generate_blocking (previous); - } - nano::send_block send (previous, destination, balance.number () - amount.number (), prv, pub, work); - response_l.put ("hash", send.hash ().to_string ()); - std::string contents; - send.serialize_json (contents); - response_l.put ("block", contents); - } - else - { - ec = nano::error_common::insufficient_balance; - } - } - else - { - ec = nano::error_rpc::block_create_requirements_send; - } - } - else - { - ec = nano::error_blocks::invalid_type; - } - } - else - { - ec = nano::error_rpc::block_create_key_required; - } - } - response_errors (); -} - -void nano::rpc_handler::block_hash () -{ - const bool json_block_l = request.get ("json_block", false); - std::shared_ptr block; - if (json_block_l) - { - block = block_json_impl (true); - } - else - { - block = block_impl (true); - } - - if (!ec) - { - response_l.put ("hash", block->hash ().to_string ()); - } - response_errors (); -} - -void nano::rpc_handler::bootstrap () -{ - std::string address_text = request.get ("address"); - std::string port_text = request.get ("port"); - boost::system::error_code address_ec; - auto address (boost::asio::ip::address_v6::from_string (address_text, address_ec)); - if (!address_ec) - { - uint16_t port; - if (!nano::parse_port (port_text, port)) - { - node.bootstrap_initiator.bootstrap (nano::endpoint (address, port)); - response_l.put ("success", ""); - } - else - { - ec = nano::error_common::invalid_port; - } - } - else - { - ec = nano::error_common::invalid_ip_address; - } - response_errors (); -} - -void nano::rpc_handler::bootstrap_any () -{ - node.bootstrap_initiator.bootstrap (); - response_l.put ("success", ""); - response_errors (); -} - -void nano::rpc_handler::bootstrap_lazy () -{ - rpc_control_impl (); - auto hash (hash_impl ()); - const bool force = request.get ("force", false); - if (!ec) - { - node.bootstrap_initiator.bootstrap_lazy (hash, force); - response_l.put ("started", "1"); - } - response_errors (); -} - -/* - * @warning This is an internal/diagnostic RPC, do not rely on its interface being stable - */ -void nano::rpc_handler::bootstrap_status () -{ - auto attempt (node.bootstrap_initiator.current_attempt ()); - if (attempt != nullptr) - { - response_l.put ("clients", std::to_string (attempt->clients.size ())); - response_l.put ("pulls", std::to_string (attempt->pulls.size ())); - response_l.put ("pulling", std::to_string (attempt->pulling)); - response_l.put ("connections", std::to_string (attempt->connections)); - response_l.put ("idle", std::to_string (attempt->idle.size ())); - response_l.put ("target_connections", std::to_string (attempt->target_connections (attempt->pulls.size ()))); - response_l.put ("total_blocks", std::to_string (attempt->total_blocks)); - response_l.put ("runs_count", std::to_string (attempt->runs_count)); - std::string mode_text; - if (attempt->mode == nano::bootstrap_mode::legacy) - { - mode_text = "legacy"; - } - else if (attempt->mode == nano::bootstrap_mode::lazy) - { - mode_text = "lazy"; - } - else if (attempt->mode == nano::bootstrap_mode::wallet_lazy) - { - mode_text = "wallet_lazy"; - } - response_l.put ("mode", mode_text); - response_l.put ("lazy_blocks", std::to_string (attempt->lazy_blocks.size ())); - response_l.put ("lazy_state_unknown", std::to_string (attempt->lazy_state_unknown.size ())); - response_l.put ("lazy_balances", std::to_string (attempt->lazy_balances.size ())); - response_l.put ("lazy_pulls", std::to_string (attempt->lazy_pulls.size ())); - response_l.put ("lazy_stopped", std::to_string (attempt->lazy_stopped)); - response_l.put ("lazy_keys", std::to_string (attempt->lazy_keys.size ())); - if (!attempt->lazy_keys.empty ()) - { - response_l.put ("lazy_key_1", (*(attempt->lazy_keys.begin ())).to_string ()); - } - } - else - { - response_l.put ("active", "0"); - } - response_errors (); -} - -void nano::rpc_handler::chain (bool successors) -{ - successors = successors != request.get ("reverse", false); - auto hash (hash_impl ("block")); - auto count (count_impl ()); - auto offset (offset_optional_impl (0)); - if (!ec) - { - boost::property_tree::ptree blocks; - auto transaction (node.store.tx_begin_read ()); - while (!hash.is_zero () && blocks.size () < count) - { - auto block_l (node.store.block_get (transaction, hash)); - if (block_l != nullptr) - { - if (offset > 0) - { - --offset; - } - else - { - boost::property_tree::ptree entry; - entry.put ("", hash.to_string ()); - blocks.push_back (std::make_pair ("", entry)); - } - hash = successors ? node.store.block_successor (transaction, hash) : block_l->previous (); - } - else - { - hash.clear (); - } - } - response_l.add_child ("blocks", blocks); - } - response_errors (); -} - -void nano::rpc_handler::confirmation_active () -{ - uint64_t announcements (0); - boost::optional announcements_text (request.get_optional ("announcements")); - if (announcements_text.is_initialized ()) - { - announcements = strtoul (announcements_text.get ().c_str (), NULL, 10); - } - boost::property_tree::ptree elections; - { - std::lock_guard lock (node.active.mutex); - for (auto i (node.active.roots.begin ()), n (node.active.roots.end ()); i != n; ++i) - { - if (i->election->announcements >= announcements && !i->election->confirmed && !i->election->stopped) - { - boost::property_tree::ptree entry; - entry.put ("", i->root.to_string ()); - elections.push_back (std::make_pair ("", entry)); - } - } - } - response_l.add_child ("confirmations", elections); - response_errors (); -} - -void nano::rpc_handler::confirmation_history () -{ - boost::property_tree::ptree elections; - boost::property_tree::ptree confirmation_stats; - std::chrono::milliseconds running_total (0); - nano::block_hash hash (0); - boost::optional hash_text (request.get_optional ("hash")); - if (hash_text.is_initialized ()) - { - hash = hash_impl (); - } - if (!ec) - { - auto confirmed (node.active.list_confirmed ()); - for (auto i (confirmed.begin ()), n (confirmed.end ()); i != n; ++i) - { - if (hash.is_zero () || i->winner->hash () == hash) - { - boost::property_tree::ptree election; - election.put ("hash", i->winner->hash ().to_string ()); - election.put ("duration", i->election_duration.count ()); - election.put ("time", i->election_end.count ()); - election.put ("tally", i->tally.to_string_dec ()); - elections.push_back (std::make_pair ("", election)); - } - running_total += i->election_duration; - } - } - confirmation_stats.put ("count", elections.size ()); - if (elections.size () >= 1) - { - confirmation_stats.put ("average", (running_total.count ()) / elections.size ()); - } - response_l.add_child ("confirmation_stats", confirmation_stats); - response_l.add_child ("confirmations", elections); - response_errors (); -} - -void nano::rpc_handler::confirmation_info () -{ - const bool representatives = request.get ("representatives", false); - const bool contents = request.get ("contents", true); - const bool json_block_l = request.get ("json_block", false); - std::string root_text (request.get ("root")); - nano::qualified_root root; - if (!root.decode_hex (root_text)) - { - std::lock_guard lock (node.active.mutex); - auto conflict_info (node.active.roots.find (root)); - if (conflict_info != node.active.roots.end ()) - { - response_l.put ("announcements", std::to_string (conflict_info->election->announcements)); - auto election (conflict_info->election); - nano::uint128_t total (0); - response_l.put ("last_winner", election->status.winner->hash ().to_string ()); - auto transaction (node.store.tx_begin_read ()); - auto tally_l (election->tally (transaction)); - boost::property_tree::ptree blocks; - for (auto i (tally_l.begin ()), n (tally_l.end ()); i != n; ++i) - { - boost::property_tree::ptree entry; - auto tally (i->first); - entry.put ("tally", tally.convert_to ()); - total += tally; - if (contents) - { - if (json_block_l) - { - boost::property_tree::ptree block_node_l; - i->second->serialize_json (block_node_l); - entry.add_child ("contents", block_node_l); - } - else - { - std::string contents; - i->second->serialize_json (contents); - entry.put ("contents", contents); - } - } - if (representatives) - { - std::multimap> representatives; - for (auto ii (election->last_votes.begin ()), nn (election->last_votes.end ()); ii != nn; ++ii) - { - if (i->second->hash () == ii->second.hash) - { - nano::account representative (ii->first); - auto amount (node.store.representation_get (transaction, representative)); - representatives.insert (std::make_pair (amount, representative)); - } - } - boost::property_tree::ptree representatives_list; - for (auto ii (representatives.begin ()), nn (representatives.end ()); ii != nn; ++ii) - { - representatives_list.put (ii->second.to_account (), ii->first.convert_to ()); - } - entry.add_child ("representatives", representatives_list); - } - blocks.add_child ((i->second->hash ()).to_string (), entry); - } - response_l.put ("total_tally", total.convert_to ()); - response_l.add_child ("blocks", blocks); - } - else - { - ec = nano::error_rpc::confirmation_not_found; - } - } - else - { - ec = nano::error_rpc::invalid_root; - } - response_errors (); -} - -void nano::rpc_handler::confirmation_quorum () -{ - response_l.put ("quorum_delta", node.delta ().convert_to ()); - response_l.put ("online_weight_quorum_percent", std::to_string (node.config.online_weight_quorum)); - response_l.put ("online_weight_minimum", node.config.online_weight_minimum.to_string_dec ()); - response_l.put ("online_stake_total", node.online_reps.online_stake ().convert_to ()); - response_l.put ("peers_stake_total", node.rep_crawler.total_weight ().convert_to ()); - response_l.put ("peers_stake_required", std::max (node.config.online_weight_minimum.number (), node.delta ()).convert_to ()); - if (request.get ("peer_details", false)) - { - boost::property_tree::ptree peers; - for (auto & peer : node.rep_crawler.representatives_by_weight ()) - { - boost::property_tree::ptree peer_node; - peer_node.put ("account", peer.account.to_account ()); - peer_node.put ("ip", peer.channel->to_string ()); - peer_node.put ("weight", peer.weight.to_string_dec ()); - peers.push_back (std::make_pair ("", peer_node)); - } - response_l.add_child ("peers", peers); - } - response_errors (); -} - -void nano::rpc_handler::delegators () -{ - auto account (account_impl ()); - if (!ec) - { - boost::property_tree::ptree delegators; - auto transaction (node.store.tx_begin_read ()); - for (auto i (node.store.latest_begin (transaction)), n (node.store.latest_end ()); i != n; ++i) - { - nano::account_info info (i->second); - auto block (node.store.block_get (transaction, info.rep_block)); - assert (block != nullptr); - if (block->representative () == account) - { - std::string balance; - nano::uint128_union (info.balance).encode_dec (balance); - delegators.put (nano::account (i->first).to_account (), balance); - } - } - response_l.add_child ("delegators", delegators); - } - response_errors (); -} - -void nano::rpc_handler::delegators_count () -{ - auto account (account_impl ()); - if (!ec) - { - uint64_t count (0); - auto transaction (node.store.tx_begin_read ()); - for (auto i (node.store.latest_begin (transaction)), n (node.store.latest_end ()); i != n; ++i) - { - nano::account_info info (i->second); - auto block (node.store.block_get (transaction, info.rep_block)); - assert (block != nullptr); - if (block->representative () == account) - { - ++count; - } - } - response_l.put ("count", std::to_string (count)); - } - response_errors (); -} - -void nano::rpc_handler::deterministic_key () -{ - std::string seed_text (request.get ("seed")); - std::string index_text (request.get ("index")); - nano::raw_key seed; - if (!seed.data.decode_hex (seed_text)) - { - try - { - uint32_t index (std::stoul (index_text)); - nano::uint256_union prv; - nano::deterministic_key (seed.data, index, prv); - nano::uint256_union pub (nano::pub_key (prv)); - response_l.put ("private", prv.to_string ()); - response_l.put ("public", pub.to_string ()); - response_l.put ("account", pub.to_account ()); - } - catch (std::logic_error const &) - { - ec = nano::error_common::invalid_index; - } - } - else - { - ec = nano::error_common::bad_seed; - } - response_errors (); -} - -void nano::rpc_handler::frontiers () -{ - auto start (account_impl ()); - auto count (count_impl ()); - if (!ec) - { - boost::property_tree::ptree frontiers; - auto transaction (node.store.tx_begin_read ()); - for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n && frontiers.size () < count; ++i) - { - frontiers.put (nano::account (i->first).to_account (), nano::account_info (i->second).head.to_string ()); - } - response_l.add_child ("frontiers", frontiers); - } - response_errors (); -} - -void nano::rpc_handler::account_count () -{ - auto transaction (node.store.tx_begin_read ()); - auto size (node.store.account_count (transaction)); - response_l.put ("count", std::to_string (size)); - response_errors (); } namespace { -class history_visitor : public nano::block_visitor -{ -public: - history_visitor (nano::rpc_handler & handler_a, bool raw_a, nano::transaction & transaction_a, boost::property_tree::ptree & tree_a, nano::block_hash const & hash_a, std::vector const & accounts_filter_a) : - handler (handler_a), - raw (raw_a), - transaction (transaction_a), - tree (tree_a), - hash (hash_a), - accounts_filter (accounts_filter_a) - { - } - virtual ~history_visitor () = default; - void send_block (nano::send_block const & block_a) - { - if (should_ignore_account (block_a.hashables.destination)) - { - return; - } - tree.put ("type", "send"); - auto account (block_a.hashables.destination.to_account ()); - tree.put ("account", account); - auto amount (handler.node.ledger.amount (transaction, hash).convert_to ()); - tree.put ("amount", amount); - if (raw) - { - tree.put ("destination", account); - tree.put ("balance", block_a.hashables.balance.to_string_dec ()); - tree.put ("previous", block_a.hashables.previous.to_string ()); - } - } - void receive_block (nano::receive_block const & block_a) - { - if (should_ignore_account (block_a.hashables.source)) - { - return; - } - tree.put ("type", "receive"); - auto account (handler.node.ledger.account (transaction, block_a.hashables.source).to_account ()); - tree.put ("account", account); - auto amount (handler.node.ledger.amount (transaction, hash).convert_to ()); - tree.put ("amount", amount); - if (raw) - { - tree.put ("source", block_a.hashables.source.to_string ()); - tree.put ("previous", block_a.hashables.previous.to_string ()); - } - } - void open_block (nano::open_block const & block_a) - { - if (should_ignore_account (block_a.hashables.source)) - { - return; - } - if (raw) - { - tree.put ("type", "open"); - tree.put ("representative", block_a.hashables.representative.to_account ()); - tree.put ("source", block_a.hashables.source.to_string ()); - tree.put ("opened", block_a.hashables.account.to_account ()); - } - else - { - // Report opens as a receive - tree.put ("type", "receive"); - } - if (block_a.hashables.source != network_params.ledger.genesis_account) - { - tree.put ("account", handler.node.ledger.account (transaction, block_a.hashables.source).to_account ()); - tree.put ("amount", handler.node.ledger.amount (transaction, hash).convert_to ()); - } - else - { - tree.put ("account", network_params.ledger.genesis_account.to_account ()); - tree.put ("amount", network_params.ledger.genesis_amount.convert_to ()); - } - } - void change_block (nano::change_block const & block_a) - { - if (raw && accounts_filter.empty ()) - { - tree.put ("type", "change"); - tree.put ("representative", block_a.hashables.representative.to_account ()); - tree.put ("previous", block_a.hashables.previous.to_string ()); - } - } - void state_block (nano::state_block const & block_a) - { - if (raw) - { - tree.put ("type", "state"); - tree.put ("representative", block_a.hashables.representative.to_account ()); - tree.put ("link", block_a.hashables.link.to_string ()); - tree.put ("balance", block_a.hashables.balance.to_string_dec ()); - tree.put ("previous", block_a.hashables.previous.to_string ()); - } - auto balance (block_a.hashables.balance.number ()); - auto previous_balance (handler.node.ledger.balance (transaction, block_a.hashables.previous)); - if (balance < previous_balance) - { - if (should_ignore_account (block_a.hashables.link)) - { - tree.clear (); - return; - } - if (raw) - { - tree.put ("subtype", "send"); - } - else - { - tree.put ("type", "send"); - } - tree.put ("account", block_a.hashables.link.to_account ()); - tree.put ("amount", (previous_balance - balance).convert_to ()); - } - else - { - if (block_a.hashables.link.is_zero ()) - { - if (raw && accounts_filter.empty ()) - { - tree.put ("subtype", "change"); - } - } - else if (balance == previous_balance && !handler.node.ledger.epoch_link.is_zero () && handler.node.ledger.is_epoch_link (block_a.hashables.link)) - { - if (raw && accounts_filter.empty ()) - { - tree.put ("subtype", "epoch"); - tree.put ("account", handler.node.ledger.epoch_signer.to_account ()); - } - } - else - { - if (should_ignore_account (block_a.hashables.link)) - { - tree.clear (); - return; - } - if (raw) - { - tree.put ("subtype", "receive"); - } - else - { - tree.put ("type", "receive"); - } - tree.put ("account", handler.node.ledger.account (transaction, block_a.hashables.link).to_account ()); - tree.put ("amount", (balance - previous_balance).convert_to ()); - } - } - } - bool should_ignore_account (nano::public_key const & account) - { - bool ignore (false); - if (!accounts_filter.empty ()) - { - if (std::find (accounts_filter.begin (), accounts_filter.end (), account) == accounts_filter.end ()) - { - ignore = true; - } - } - return ignore; - } - nano::rpc_handler & handler; - bool raw; - nano::transaction & transaction; - boost::property_tree::ptree & tree; - nano::block_hash const & hash; - nano::network_params network_params; - std::vector const & accounts_filter; -}; -} - -void nano::rpc_handler::account_history () -{ - std::vector accounts_to_filter; - const auto accounts_filter_node = request.get_child_optional ("account_filter"); - if (accounts_filter_node.is_initialized ()) - { - for (auto & a : (*accounts_filter_node)) - { - nano::public_key account; - auto error (account.decode_account (a.second.get (""))); - if (!error) - { - accounts_to_filter.push_back (account); - } - else - { - ec = nano::error_common::bad_account_number; - break; - } - } - } - nano::account account; - nano::block_hash hash; - bool output_raw (request.get_optional ("raw") == true); - bool reverse (request.get_optional ("reverse") == true); - auto head_str (request.get_optional ("head")); - auto transaction (node.store.tx_begin_read ()); - auto count (count_impl ()); - auto offset (offset_optional_impl (0)); - if (head_str) - { - if (!hash.decode_hex (*head_str)) - { - if (node.store.block_exists (transaction, hash)) - { - account = node.ledger.account (transaction, hash); - } - else - { - ec = nano::error_blocks::not_found; - } - } - else - { - ec = nano::error_blocks::bad_hash_number; - } - } - else - { - account = account_impl (); - if (!ec) - { - if (reverse) - { - nano::account_info info; - if (!node.store.account_get (transaction, account, info)) - { - hash = info.open_block; - } - else - { - ec = nano::error_common::account_not_found; - } - } - else - { - hash = node.ledger.latest (transaction, account); - } - } - } - if (!ec) - { - boost::property_tree::ptree history; - response_l.put ("account", account.to_account ()); - nano::block_sideband sideband; - auto block (node.store.block_get (transaction, hash, &sideband)); - while (block != nullptr && count > 0) - { - if (offset > 0) - { - --offset; - } - else - { - boost::property_tree::ptree entry; - history_visitor visitor (*this, output_raw, transaction, entry, hash, accounts_to_filter); - block->visit (visitor); - if (!entry.empty ()) - { - entry.put ("local_timestamp", std::to_string (sideband.timestamp)); - entry.put ("height", std::to_string (sideband.height)); - entry.put ("hash", hash.to_string ()); - if (output_raw) - { - entry.put ("work", nano::to_string_hex (block->block_work ())); - entry.put ("signature", block->block_signature ().to_string ()); - } - history.push_back (std::make_pair ("", entry)); - --count; - } - } - hash = reverse ? node.store.block_successor (transaction, hash) : block->previous (); - block = node.store.block_get (transaction, hash, &sideband); - } - response_l.add_child ("history", history); - if (!hash.is_zero ()) - { - response_l.put (reverse ? "next" : "previous", hash.to_string ()); - } - } - response_errors (); -} - -void nano::rpc_handler::keepalive () -{ - rpc_control_impl (); - if (!ec) - { - std::string address_text (request.get ("address")); - std::string port_text (request.get ("port")); - uint16_t port; - if (!nano::parse_port (port_text, port)) - { - node.keepalive (address_text, port); - response_l.put ("started", "1"); - } - else - { - ec = nano::error_common::invalid_port; - } - } - response_errors (); -} - -void nano::rpc_handler::key_create () -{ - nano::keypair pair; - response_l.put ("private", pair.prv.data.to_string ()); - response_l.put ("public", pair.pub.to_string ()); - response_l.put ("account", pair.pub.to_account ()); - response_errors (); -} - -void nano::rpc_handler::key_expand () -{ - std::string key_text (request.get ("key")); - nano::uint256_union prv; - if (!prv.decode_hex (key_text)) - { - nano::uint256_union pub (nano::pub_key (prv)); - response_l.put ("private", prv.to_string ()); - response_l.put ("public", pub.to_string ()); - response_l.put ("account", pub.to_account ()); - } - else - { - ec = nano::error_common::bad_private_key; - } - response_errors (); -} - -void nano::rpc_handler::ledger () -{ - rpc_control_impl (); - auto count (count_optional_impl ()); - if (!ec) - { - nano::account start (0); - boost::optional account_text (request.get_optional ("account")); - if (account_text.is_initialized ()) - { - if (start.decode_account (account_text.get ())) - { - ec = nano::error_common::bad_account_number; - } - } - uint64_t modified_since (0); - boost::optional modified_since_text (request.get_optional ("modified_since")); - if (modified_since_text.is_initialized ()) - { - if (decode_unsigned (modified_since_text.get (), modified_since)) - { - ec = nano::error_rpc::invalid_timestamp; - } - } - const bool sorting = request.get ("sorting", false); - const bool representative = request.get ("representative", false); - const bool weight = request.get ("weight", false); - const bool pending = request.get ("pending", false); - boost::property_tree::ptree accounts; - auto transaction (node.store.tx_begin_read ()); - if (!ec && !sorting) // Simple - { - for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n && accounts.size () < count; ++i) - { - nano::account_info info (i->second); - if (info.modified >= modified_since) - { - nano::account account (i->first); - boost::property_tree::ptree response_a; - response_a.put ("frontier", info.head.to_string ()); - response_a.put ("open_block", info.open_block.to_string ()); - response_a.put ("representative_block", info.rep_block.to_string ()); - std::string balance; - nano::uint128_union (info.balance).encode_dec (balance); - response_a.put ("balance", balance); - response_a.put ("modified_timestamp", std::to_string (info.modified)); - response_a.put ("block_count", std::to_string (info.block_count)); - if (representative) - { - auto block (node.store.block_get (transaction, info.rep_block)); - assert (block != nullptr); - response_a.put ("representative", block->representative ().to_account ()); - } - if (weight) - { - auto account_weight (node.ledger.weight (transaction, account)); - response_a.put ("weight", account_weight.convert_to ()); - } - if (pending) - { - auto account_pending (node.ledger.account_pending (transaction, account)); - response_a.put ("pending", account_pending.convert_to ()); - } - accounts.push_back (std::make_pair (account.to_account (), response_a)); - } - } - } - else if (!ec) // Sorting - { - std::vector> ledger_l; - for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n; ++i) - { - nano::account_info info (i->second); - nano::uint128_union balance (info.balance); - if (info.modified >= modified_since) - { - ledger_l.push_back (std::make_pair (balance, nano::account (i->first))); - } - } - std::sort (ledger_l.begin (), ledger_l.end ()); - std::reverse (ledger_l.begin (), ledger_l.end ()); - nano::account_info info; - for (auto i (ledger_l.begin ()), n (ledger_l.end ()); i != n && accounts.size () < count; ++i) - { - node.store.account_get (transaction, i->second, info); - nano::account account (i->second); - boost::property_tree::ptree response_a; - response_a.put ("frontier", info.head.to_string ()); - response_a.put ("open_block", info.open_block.to_string ()); - response_a.put ("representative_block", info.rep_block.to_string ()); - std::string balance; - (i->first).encode_dec (balance); - response_a.put ("balance", balance); - response_a.put ("modified_timestamp", std::to_string (info.modified)); - response_a.put ("block_count", std::to_string (info.block_count)); - if (representative) - { - auto block (node.store.block_get (transaction, info.rep_block)); - assert (block != nullptr); - response_a.put ("representative", block->representative ().to_account ()); - } - if (weight) - { - auto account_weight (node.ledger.weight (transaction, account)); - response_a.put ("weight", account_weight.convert_to ()); - } - if (pending) - { - auto account_pending (node.ledger.account_pending (transaction, account)); - response_a.put ("pending", account_pending.convert_to ()); - } - accounts.push_back (std::make_pair (account.to_account (), response_a)); - } - } - response_l.add_child ("accounts", accounts); - } - response_errors (); -} - -void nano::rpc_handler::mnano_from_raw (nano::uint128_t ratio) -{ - auto amount (amount_impl ()); - if (!ec) - { - auto result (amount.number () / ratio); - response_l.put ("amount", result.convert_to ()); - } - response_errors (); -} - -void nano::rpc_handler::mnano_to_raw (nano::uint128_t ratio) -{ - auto amount (amount_impl ()); - if (!ec) - { - auto result (amount.number () * ratio); - if (result > amount.number ()) - { - response_l.put ("amount", result.convert_to ()); - } - else - { - ec = nano::error_common::invalid_amount_big; - } - } - response_errors (); -} - -/* - * @warning This is an internal/diagnostic RPC, do not rely on its interface being stable - */ -void nano::rpc_handler::node_id () -{ - rpc_control_impl (); - if (!ec) - { - response_l.put ("private", node.node_id.prv.data.to_string ()); - response_l.put ("public", node.node_id.pub.to_string ()); - response_l.put ("as_account", node.node_id.pub.to_account ()); - } - response_errors (); -} - -/* - * @warning This is an internal/diagnostic RPC, do not rely on its interface being stable - */ -void nano::rpc_handler::node_id_delete () -{ - response_l.put ("deprecated", "1"); - response_errors (); -} - -void nano::rpc_handler::password_change () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_write ()); - wallet_locked_impl (transaction, wallet); - if (!ec) - { - std::string password_text (request.get ("password")); - bool error (wallet->store.rekey (transaction, password_text)); - response_l.put ("changed", error ? "0" : "1"); - if (!error) - { - node.logger.try_log ("Wallet password changed"); - } - } - } - response_errors (); -} - -void nano::rpc_handler::password_enter () -{ - auto wallet (wallet_impl ()); - if (!ec) - { - std::string password_text (request.get ("password")); - auto transaction (wallet->wallets.tx_begin_write ()); - auto error (wallet->enter_password (transaction, password_text)); - response_l.put ("valid", error ? "0" : "1"); - } - response_errors (); +std::unordered_set create_rpc_control_impls () +{ + std::unordered_set set; + set.emplace ("account_create"); + set.emplace ("account_move"); + set.emplace ("account_remove"); + set.emplace ("account_representative_set"); + set.emplace ("accounts_create"); + set.emplace ("block_create"); + set.emplace ("bootstrap_lazy"); + set.emplace ("keepalive"); + set.emplace ("ledger"); + set.emplace ("node_id"); + set.emplace ("password_change"); + set.emplace ("receive"); + set.emplace ("receive_minimum"); + set.emplace ("receive_minimum_set"); + set.emplace ("search_pending"); + set.emplace ("search_pending_all"); + set.emplace ("send"); + set.emplace ("stop"); + set.emplace ("unchecked_clear"); + set.emplace ("unopened"); + set.emplace ("wallet_add"); + set.emplace ("wallet_add_watch"); + set.emplace ("wallet_change_seed"); + set.emplace ("wallet_create"); + set.emplace ("wallet_destroy"); + set.emplace ("wallet_lock"); + set.emplace ("wallet_representative_set"); + set.emplace ("wallet_republish"); + set.emplace ("wallet_work_get"); + set.emplace ("work_generate"); + set.emplace ("work_cancel"); + set.emplace ("work_get"); + set.emplace ("work_set"); + set.emplace ("work_peer_add"); + set.emplace ("work_peers"); + set.emplace ("work_peers_clear"); + set.emplace ("wallet_seed"); + return set; } -void nano::rpc_handler::password_valid (bool wallet_locked) -{ - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - auto valid (wallet->store.valid_password (transaction)); - if (!wallet_locked) - { - response_l.put ("valid", valid ? "1" : "0"); - } - else - { - response_l.put ("locked", valid ? "0" : "1"); - } - } - response_errors (); -} - -void nano::rpc_handler::peers () -{ - boost::property_tree::ptree peers_l; - const bool peer_details = request.get ("peer_details", false); - auto peers_list (node.network.udp_channels.list (std::numeric_limits::max ())); - std::sort (peers_list.begin (), peers_list.end ()); - for (auto i (peers_list.begin ()), n (peers_list.end ()); i != n; ++i) - { - std::stringstream text; - auto channel (*i); - text << channel->to_string (); - if (peer_details) - { - boost::property_tree::ptree pending_tree; - pending_tree.put ("protocol_version", std::to_string (channel->network_version)); - if (channel->node_id.is_initialized ()) - { - pending_tree.put ("node_id", channel->node_id.get ().to_account ()); - } - else - { - pending_tree.put ("node_id", ""); - } - peers_l.push_back (boost::property_tree::ptree::value_type (text.str (), pending_tree)); - } - else - { - peers_l.push_back (boost::property_tree::ptree::value_type (text.str (), boost::property_tree::ptree (std::to_string (channel->network_version)))); - } - } - response_l.add_child ("peers", peers_l); - response_errors (); -} - -void nano::rpc_handler::pending () -{ - auto account (account_impl ()); - auto count (count_optional_impl ()); - auto threshold (threshold_optional_impl ()); - const bool source = request.get ("source", false); - const bool min_version = request.get ("min_version", false); - const bool include_active = request.get ("include_active", false); - const bool sorting = request.get ("sorting", false); - auto simple (threshold.is_zero () && !source && !min_version && !sorting); // if simple, response is a list of hashes - if (!ec) - { - boost::property_tree::ptree peers_l; - auto transaction (node.store.tx_begin_read ()); - for (auto i (node.store.pending_begin (transaction, nano::pending_key (account, 0))); nano::pending_key (i->first).account == account && peers_l.size () < count; ++i) - { - nano::pending_key key (i->first); - if (include_active || node.ledger.block_confirmed (transaction, key.hash)) - { - if (simple) - { - boost::property_tree::ptree entry; - entry.put ("", key.hash.to_string ()); - peers_l.push_back (std::make_pair ("", entry)); - } - else - { - nano::pending_info info (i->second); - if (info.amount.number () >= threshold.number ()) - { - if (source || min_version) - { - boost::property_tree::ptree pending_tree; - pending_tree.put ("amount", info.amount.number ().convert_to ()); - if (source) - { - pending_tree.put ("source", info.source.to_account ()); - } - if (min_version) - { - pending_tree.put ("min_version", info.epoch == nano::epoch::epoch_1 ? "1" : "0"); - } - peers_l.add_child (key.hash.to_string (), pending_tree); - } - else - { - peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); - } - } - } - } - } - if (sorting && !simple) - { - if (source || min_version) - { - peers_l.sort ([](const auto & child1, const auto & child2) -> bool { - return child1.second.template get ("amount") > child2.second.template get ("amount"); - }); - } - else - { - peers_l.sort ([](const auto & child1, const auto & child2) -> bool { - return child1.second.template get ("") > child2.second.template get (""); - }); - } - } - response_l.add_child ("blocks", peers_l); - } - response_errors (); -} - -void nano::rpc_handler::pending_exists () -{ - auto hash (hash_impl ()); - const bool include_active = request.get ("include_active", false); - if (!ec) - { - auto transaction (node.store.tx_begin_read ()); - auto block (node.store.block_get (transaction, hash)); - if (block != nullptr) - { - auto exists (false); - auto destination (node.ledger.block_destination (transaction, *block)); - if (!destination.is_zero ()) - { - exists = node.store.pending_exists (transaction, nano::pending_key (destination, hash)); - } - exists = exists && (include_active || node.ledger.block_confirmed (transaction, hash)); - response_l.put ("exists", exists ? "1" : "0"); - } - else - { - ec = nano::error_blocks::not_found; - } - } - response_errors (); -} - -void nano::rpc_handler::payment_begin () -{ - std::string id_text (request.get ("wallet")); - nano::uint256_union id; - if (!id.decode_hex (id_text)) - { - auto existing (node.wallets.items.find (id)); - if (existing != node.wallets.items.end ()) - { - auto transaction (node.wallets.tx_begin_write ()); - std::shared_ptr wallet (existing->second); - if (wallet->store.valid_password (transaction)) - { - nano::account account (0); - do - { - auto existing (wallet->free_accounts.begin ()); - if (existing != wallet->free_accounts.end ()) - { - account = *existing; - wallet->free_accounts.erase (existing); - if (wallet->store.find (transaction, account) == wallet->store.end ()) - { - node.logger.always_log (boost::str (boost::format ("Transaction wallet %1% externally modified listing account %2% as free but no longer exists") % id.to_string () % account.to_account ())); - account.clear (); - } - else - { - auto block_transaction (node.store.tx_begin_read ()); - if (!node.ledger.account_balance (block_transaction, account).is_zero ()) - { - node.logger.always_log (boost::str (boost::format ("Skipping account %1% for use as a transaction account: non-zero balance") % account.to_account ())); - account.clear (); - } - } - } - else - { - account = wallet->deterministic_insert (transaction); - break; - } - } while (account.is_zero ()); - if (!account.is_zero ()) - { - response_l.put ("deprecated", "1"); - response_l.put ("account", account.to_account ()); - } - else - { - ec = nano::error_rpc::payment_unable_create_account; - } - } - else - { - ec = nano::error_common::wallet_locked; - } - } - else - { - ec = nano::error_common::wallet_not_found; - } - } - else - { - ec = nano::error_common::bad_wallet_number; - } - response_errors (); -} - -void nano::rpc_handler::payment_init () -{ - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_write ()); - if (wallet->store.valid_password (transaction)) - { - wallet->init_free_accounts (transaction); - response_l.put ("deprecated", "1"); - response_l.put ("status", "Ready"); - } - else - { - ec = nano::error_common::wallet_locked; - } - } - response_errors (); -} - -void nano::rpc_handler::payment_end () -{ - auto account (account_impl ()); - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - wallet_account_impl (transaction, wallet, account); - if (!ec) - { - if (node.ledger.account_balance (block_transaction, account).is_zero ()) - { - wallet->free_accounts.insert (account); - response_l.put ("deprecated", "1"); - response_l.put ("ended", "1"); - } - else - { - ec = nano::error_rpc::payment_account_balance; - } - } - } - response_errors (); -} - -void nano::rpc_handler::payment_wait () -{ - std::string timeout_text (request.get ("timeout")); - auto account (account_impl ()); - auto amount (amount_impl ()); - if (!ec) - { - uint64_t timeout; - if (!decode_unsigned (timeout_text, timeout)) - { - { - auto observer (std::make_shared (response, rpc, account, amount)); - observer->start (timeout); - std::lock_guard lock (rpc.mutex); - assert (rpc.payment_observers.find (account) == rpc.payment_observers.end ()); - rpc.payment_observers[account] = observer; - } - rpc.observer_action (account); - } - else - { - ec = nano::error_rpc::bad_timeout; - } - } - if (ec) - { - response_errors (); - } -} - -void nano::rpc_handler::process () -{ - const bool json_block_l = request.get ("json_block", false); - std::shared_ptr block; - if (json_block_l) - { - block = block_json_impl (true); - } - else - { - block = block_impl (true); - } - - // State blocks subtype check - if (!ec && block->type () == nano::block_type::state) - { - std::string subtype_text (request.get ("subtype", "")); - if (!subtype_text.empty ()) - { - std::shared_ptr block_state (std::static_pointer_cast (block)); - auto transaction (node.store.tx_begin_read ()); - if (!block_state->hashables.previous.is_zero () && !node.store.block_exists (transaction, block_state->hashables.previous)) - { - ec = nano::error_process::gap_previous; - } - else - { - auto balance (node.ledger.account_balance (transaction, block_state->hashables.account)); - if (subtype_text == "send") - { - if (balance <= block_state->hashables.balance.number ()) - { - ec = nano::error_rpc::invalid_subtype_balance; - } - // Send with previous == 0 fails balance check. No previous != 0 check required - } - else if (subtype_text == "receive") - { - if (balance > block_state->hashables.balance.number ()) - { - ec = nano::error_rpc::invalid_subtype_balance; - } - // Receive can be point to open block. No previous != 0 check required - } - else if (subtype_text == "open") - { - if (!block_state->hashables.previous.is_zero ()) - { - ec = nano::error_rpc::invalid_subtype_previous; - } - } - else if (subtype_text == "change") - { - if (balance != block_state->hashables.balance.number ()) - { - ec = nano::error_rpc::invalid_subtype_balance; - } - else if (block_state->hashables.previous.is_zero ()) - { - ec = nano::error_rpc::invalid_subtype_previous; - } - } - else if (subtype_text == "epoch") - { - if (balance != block_state->hashables.balance.number ()) - { - ec = nano::error_rpc::invalid_subtype_balance; - } - else if (!node.ledger.is_epoch_link (block_state->hashables.link)) - { - ec = ec = nano::error_rpc::invalid_subtype_epoch_link; - } - } - else - { - ec = nano::error_rpc::invalid_subtype; - } - } - } - } - if (!ec) - { - if (!nano::work_validate (*block)) - { - auto hash (block->hash ()); - node.block_arrival.add (hash); - nano::process_return result; - { - auto transaction (node.store.tx_begin_write ()); - // Set current time to trigger automatic rebroadcast and election - nano::unchecked_info info (block, block->account (), nano::seconds_since_epoch (), nano::signature_verification::unknown); - result = node.block_processor.process_one (transaction, info); - } - switch (result.code) - { - case nano::process_result::progress: - { - response_l.put ("hash", hash.to_string ()); - break; - } - case nano::process_result::gap_previous: - { - ec = nano::error_process::gap_previous; - break; - } - case nano::process_result::gap_source: - { - ec = nano::error_process::gap_source; - break; - } - case nano::process_result::old: - { - ec = nano::error_process::old; - break; - } - case nano::process_result::bad_signature: - { - ec = nano::error_process::bad_signature; - break; - } - case nano::process_result::negative_spend: - { - // TODO once we get RPC versioning, this should be changed to "negative spend" - ec = nano::error_process::negative_spend; - break; - } - case nano::process_result::balance_mismatch: - { - ec = nano::error_process::balance_mismatch; - break; - } - case nano::process_result::unreceivable: - { - ec = nano::error_process::unreceivable; - break; - } - case nano::process_result::block_position: - { - ec = nano::error_process::block_position; - break; - } - case nano::process_result::fork: - { - const bool force = request.get ("force", false); - if (force && rpc.config.enable_control) - { - node.active.erase (*block); - node.block_processor.force (block); - response_l.put ("hash", hash.to_string ()); - } - else - { - ec = nano::error_process::fork; - } - break; - } - default: - { - ec = nano::error_process::other; - break; - } - } - } - else - { - ec = nano::error_blocks::work_low; - } - } - response_errors (); -} - -void nano::rpc_handler::receive () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - auto account (account_impl ()); - auto hash (hash_impl ("block")); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - wallet_locked_impl (transaction, wallet); - wallet_account_impl (transaction, wallet, account); - if (!ec) - { - auto block_transaction (node.store.tx_begin_read ()); - auto block (node.store.block_get (block_transaction, hash)); - if (block != nullptr) - { - if (node.store.pending_exists (block_transaction, nano::pending_key (account, hash))) - { - auto work (work_optional_impl ()); - if (!ec && work) - { - nano::account_info info; - nano::uint256_union head; - if (!node.store.account_get (block_transaction, account, info)) - { - head = info.head; - } - else - { - head = account; - } - if (nano::work_validate (head, work)) - { - ec = nano::error_common::invalid_work; - } - } - if (!ec) - { - bool generate_work (work == 0); // Disable work generation if "work" option is provided - auto response_a (response); - // clang-format off - wallet->receive_async (std::move (block), account, node.network_params.ledger.genesis_amount, [response_a](std::shared_ptr block_a) { - if (block_a != nullptr) - { - boost::property_tree::ptree response_l; - response_l.put ("block", block_a->hash ().to_string ()); - response_a (response_l); - } - else - { - error_response (response_a, "Error generating block"); - } - }, - work, generate_work); - // clang-format on - } - } - else - { - ec = nano::error_process::unreceivable; - } - } - else - { - ec = nano::error_blocks::not_found; - } - } - } - // Because of receive_async - if (ec) - { - response_errors (); - } -} - -void nano::rpc_handler::receive_minimum () -{ - rpc_control_impl (); - if (!ec) - { - response_l.put ("amount", node.config.receive_minimum.to_string_dec ()); - } - response_errors (); -} - -void nano::rpc_handler::receive_minimum_set () -{ - rpc_control_impl (); - auto amount (amount_impl ()); - if (!ec) - { - node.config.receive_minimum = amount; - response_l.put ("success", ""); - } - response_errors (); -} - -void nano::rpc_handler::representatives () -{ - auto count (count_optional_impl ()); - if (!ec) - { - const bool sorting = request.get ("sorting", false); - boost::property_tree::ptree representatives; - auto transaction (node.store.tx_begin_read ()); - if (!sorting) // Simple - { - for (auto i (node.store.representation_begin (transaction)), n (node.store.representation_end ()); i != n && representatives.size () < count; ++i) - { - nano::account account (i->first); - auto amount (node.store.representation_get (transaction, account)); - representatives.put (account.to_account (), amount.convert_to ()); - } - } - else // Sorting - { - std::vector> representation; - for (auto i (node.store.representation_begin (transaction)), n (node.store.representation_end ()); i != n; ++i) - { - nano::account account (i->first); - auto amount (node.store.representation_get (transaction, account)); - representation.push_back (std::make_pair (amount, account.to_account ())); - } - std::sort (representation.begin (), representation.end ()); - std::reverse (representation.begin (), representation.end ()); - for (auto i (representation.begin ()), n (representation.end ()); i != n && representatives.size () < count; ++i) - { - representatives.put (i->second, (i->first).number ().convert_to ()); - } - } - response_l.add_child ("representatives", representatives); - } - response_errors (); -} - -void nano::rpc_handler::representatives_online () -{ - const auto accounts_node = request.get_child_optional ("accounts"); - const bool weight = request.get ("weight", false); - std::vector accounts_to_filter; - if (accounts_node.is_initialized ()) - { - for (auto & a : (*accounts_node)) - { - nano::public_key account; - auto error (account.decode_account (a.second.get (""))); - if (!error) - { - accounts_to_filter.push_back (account); - } - else - { - ec = nano::error_common::bad_account_number; - break; - } - } - } - if (!ec) - { - boost::property_tree::ptree representatives; - auto transaction (node.store.tx_begin_read ()); - auto reps (node.online_reps.list ()); - for (auto & i : reps) - { - if (accounts_node.is_initialized ()) - { - if (accounts_to_filter.empty ()) - { - break; - } - auto found_acc = std::find (accounts_to_filter.begin (), accounts_to_filter.end (), i); - if (found_acc == accounts_to_filter.end ()) - { - continue; - } - else - { - accounts_to_filter.erase (found_acc); - } - } - if (weight) - { - boost::property_tree::ptree weight_node; - auto account_weight (node.ledger.weight (transaction, i)); - weight_node.put ("weight", account_weight.convert_to ()); - representatives.add_child (i.to_account (), weight_node); - } - else - { - boost::property_tree::ptree entry; - entry.put ("", i.to_account ()); - representatives.push_back (std::make_pair ("", entry)); - } - } - response_l.add_child ("representatives", representatives); - } - response_errors (); -} - -void nano::rpc_handler::republish () -{ - auto count (count_optional_impl (1024U)); - uint64_t sources (0); - uint64_t destinations (0); - boost::optional sources_text (request.get_optional ("sources")); - if (!ec && sources_text.is_initialized ()) - { - if (decode_unsigned (sources_text.get (), sources)) - { - ec = nano::error_rpc::invalid_sources; - } - } - boost::optional destinations_text (request.get_optional ("destinations")); - if (!ec && destinations_text.is_initialized ()) - { - if (decode_unsigned (destinations_text.get (), destinations)) - { - ec = nano::error_rpc::invalid_destinations; - } - } - auto hash (hash_impl ()); - if (!ec) - { - boost::property_tree::ptree blocks; - auto transaction (node.store.tx_begin_read ()); - auto block (node.store.block_get (transaction, hash)); - if (block != nullptr) - { - std::deque> republish_bundle; - for (auto i (0); !hash.is_zero () && i < count; ++i) - { - block = node.store.block_get (transaction, hash); - if (sources != 0) // Republish source chain - { - nano::block_hash source (node.ledger.block_source (transaction, *block)); - auto block_a (node.store.block_get (transaction, source)); - std::vector hashes; - while (block_a != nullptr && hashes.size () < sources) - { - hashes.push_back (source); - source = block_a->previous (); - block_a = node.store.block_get (transaction, source); - } - std::reverse (hashes.begin (), hashes.end ()); - for (auto & hash_l : hashes) - { - block_a = node.store.block_get (transaction, hash_l); - republish_bundle.push_back (std::move (block_a)); - boost::property_tree::ptree entry_l; - entry_l.put ("", hash_l.to_string ()); - blocks.push_back (std::make_pair ("", entry_l)); - } - } - republish_bundle.push_back (std::move (block)); // Republish block - boost::property_tree::ptree entry; - entry.put ("", hash.to_string ()); - blocks.push_back (std::make_pair ("", entry)); - if (destinations != 0) // Republish destination chain - { - auto block_b (node.store.block_get (transaction, hash)); - auto destination (node.ledger.block_destination (transaction, *block_b)); - if (!destination.is_zero ()) - { - if (!node.store.pending_exists (transaction, nano::pending_key (destination, hash))) - { - nano::block_hash previous (node.ledger.latest (transaction, destination)); - auto block_d (node.store.block_get (transaction, previous)); - nano::block_hash source; - std::vector hashes; - while (block_d != nullptr && hash != source) - { - hashes.push_back (previous); - source = node.ledger.block_source (transaction, *block_d); - previous = block_d->previous (); - block_d = node.store.block_get (transaction, previous); - } - std::reverse (hashes.begin (), hashes.end ()); - if (hashes.size () > destinations) - { - hashes.resize (destinations); - } - for (auto & hash_l : hashes) - { - block_d = node.store.block_get (transaction, hash_l); - republish_bundle.push_back (std::move (block_d)); - boost::property_tree::ptree entry_l; - entry_l.put ("", hash_l.to_string ()); - blocks.push_back (std::make_pair ("", entry_l)); - } - } - } - } - hash = node.store.block_successor (transaction, hash); - } - node.network.flood_block_batch (republish_bundle, 25); - response_l.put ("success", ""); // obsolete - response_l.add_child ("blocks", blocks); - } - else - { - ec = nano::error_blocks::not_found; - } - } - response_errors (); -} - -void nano::rpc_handler::search_pending () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - auto error (wallet->search_pending ()); - response_l.put ("started", !error); - } - response_errors (); -} - -void nano::rpc_handler::search_pending_all () -{ - rpc_control_impl (); - if (!ec) - { - node.wallets.search_pending_all (); - response_l.put ("success", ""); - } - response_errors (); -} - -void nano::rpc_handler::send () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - auto amount (amount_impl ()); - // Sending 0 amount is invalid with state blocks - if (!ec && amount.is_zero ()) - { - ec = nano::error_common::invalid_amount; - } - if (!ec) - { - std::string source_text (request.get ("source")); - nano::account source; - if (!source.decode_account (source_text)) - { - std::string destination_text (request.get ("destination")); - nano::account destination; - if (!destination.decode_account (destination_text)) - { - auto work (work_optional_impl ()); - nano::uint128_t balance (0); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - if (wallet->store.valid_password (transaction)) - { - if (wallet->store.find (transaction, source) != wallet->store.end ()) - { - nano::account_info info; - if (!node.store.account_get (block_transaction, source, info)) - { - balance = (info.balance).number (); - } - else - { - ec = nano::error_common::account_not_found; - } - if (!ec && work) - { - if (nano::work_validate (info.head, work)) - { - ec = nano::error_common::invalid_work; - } - } - } - else - { - ec = nano::error_common::account_not_found_wallet; - } - } - else - { - ec = nano::error_common::wallet_locked; - } - } - if (!ec) - { - bool generate_work (work == 0); // Disable work generation if "work" option is provided - boost::optional send_id (request.get_optional ("id")); - auto rpc_l (shared_from_this ()); - auto response_a (response); - // clang-format off - wallet->send_async (source, destination, amount.number (), [balance, amount, response_a](std::shared_ptr block_a) { - if (block_a != nullptr) - { - boost::property_tree::ptree response_l; - response_l.put ("block", block_a->hash ().to_string ()); - response_a (response_l); - } - else - { - if (balance >= amount.number ()) - { - error_response (response_a, "Error generating block"); - } - else - { - std::error_code ec (nano::error_common::insufficient_balance); - error_response (response_a, ec.message ()); - } - } - }, - work, generate_work, send_id); - // clang-format on - } - } - else - { - ec = nano::error_rpc::bad_destination; - } - } - else - { - ec = nano::error_rpc::bad_source; - } - } - // Because of send_async - if (ec) - { - response_errors (); - } -} - -void nano::rpc_handler::sign () -{ - const bool json_block_l = request.get ("json_block", false); - // Retrieving hash - nano::block_hash hash (0); - boost::optional hash_text (request.get_optional ("hash")); - if (hash_text.is_initialized ()) - { - hash = hash_impl (); - } - // Retrieving block - std::shared_ptr block; - boost::optional block_text (request.get_optional ("block")); - if (!ec && block_text.is_initialized ()) - { - if (json_block_l) - { - block = block_json_impl (true); - } - else - { - block = block_impl (true); - } - if (block != nullptr) - { - hash = block->hash (); - } - } - - // Hash or block are not initialized - if (!ec && hash.is_zero ()) - { - ec = nano::error_blocks::invalid_block; - } - // Hash is initialized without config permission - else if (!ec && !hash.is_zero () && block == nullptr && !rpc.config.enable_sign_hash) - { - ec = nano::error_rpc::sign_hash_disabled; - } - if (!ec) - { - nano::raw_key prv; - prv.data.clear (); - // Retrieving private key from request - boost::optional key_text (request.get_optional ("key")); - if (key_text.is_initialized ()) - { - if (prv.data.decode_hex (key_text.get ())) - { - ec = nano::error_common::bad_private_key; - } - } - else - { - // Retrieving private key from wallet - boost::optional account_text (request.get_optional ("account")); - boost::optional wallet_text (request.get_optional ("wallet")); - if (wallet_text.is_initialized () && account_text.is_initialized ()) - { - auto account (account_impl ()); - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - wallet_locked_impl (transaction, wallet); - wallet_account_impl (transaction, wallet, account); - if (!ec) - { - wallet->store.fetch (transaction, account, prv); - } - } - } - } - // Signing - if (prv.data != 0) - { - nano::public_key pub (nano::pub_key (prv.data)); - nano::signature signature (nano::sign_message (prv, pub, hash)); - response_l.put ("signature", signature.to_string ()); - if (block != nullptr) - { - block->signature_set (signature); - - if (json_block_l) - { - boost::property_tree::ptree block_node_l; - block->serialize_json (block_node_l); - response_l.add_child ("block", block_node_l); - } - else - { - std::string contents; - block->serialize_json (contents); - response_l.put ("block", contents); - } - } - } - else - { - ec = nano::error_rpc::block_create_key_required; - } - } - response_errors (); -} - -void nano::rpc_handler::stats () -{ - auto sink = node.stats.log_sink_json (); - std::string type (request.get ("type", "")); - bool use_sink = false; - if (type == "counters") - { - node.stats.log_counters (*sink); - use_sink = true; - } - else if (type == "objects") - { - rpc_control_impl (); - if (!ec) - { - construct_json (collect_seq_con_info (node, "node").get (), response_l); - } - } - else if (type == "samples") - { - node.stats.log_samples (*sink); - use_sink = true; - } - else - { - ec = nano::error_rpc::invalid_missing_type; - } - if (!ec && use_sink) - { - auto stat_tree_l (*static_cast (sink->to_object ())); - stat_tree_l.put ("stat_duration_seconds", node.stats.last_reset ().count ()); - response (stat_tree_l); - } - else - { - response_errors (); - } -} - -void nano::rpc_handler::stats_clear () -{ - node.stats.clear (); - response_l.put ("success", ""); - response (response_l); -} - -void nano::rpc_handler::stop () -{ - rpc_control_impl (); - if (!ec) - { - response_l.put ("success", ""); - } - response_errors (); - if (!ec) - { - rpc.stop (); - node.stop (); - } -} - -void nano::rpc_handler::unchecked () -{ - auto count (count_optional_impl ()); - if (!ec) - { - boost::property_tree::ptree unchecked; - auto transaction (node.store.tx_begin_read ()); - for (auto i (node.store.unchecked_begin (transaction)), n (node.store.unchecked_end ()); i != n && unchecked.size () < count; ++i) - { - nano::unchecked_info info (i->second); - std::string contents; - info.block->serialize_json (contents); - unchecked.put (info.block->hash ().to_string (), contents); - } - response_l.add_child ("blocks", unchecked); - } - response_errors (); -} - -void nano::rpc_handler::unchecked_clear () -{ - rpc_control_impl (); - if (!ec) - { - auto transaction (node.store.tx_begin_write ()); - node.store.unchecked_clear (transaction); - response_l.put ("success", ""); - } - response_errors (); -} - -void nano::rpc_handler::unchecked_get () -{ - const bool json_block_l = request.get ("json_block", false); - auto hash (hash_impl ()); - if (!ec) - { - auto transaction (node.store.tx_begin_read ()); - for (auto i (node.store.unchecked_begin (transaction)), n (node.store.unchecked_end ()); i != n; ++i) - { - nano::unchecked_key key (i->first); - if (key.hash == hash) - { - nano::unchecked_info info (i->second); - response_l.put ("modified_timestamp", std::to_string (info.modified)); - - if (json_block_l) - { - boost::property_tree::ptree block_node_l; - info.block->serialize_json (block_node_l); - response_l.add_child ("contents", block_node_l); - } - else - { - std::string contents; - info.block->serialize_json (contents); - response_l.put ("contents", contents); - } - break; - } - } - if (response_l.empty ()) - { - ec = nano::error_blocks::not_found; - } - } - response_errors (); -} - -void nano::rpc_handler::unchecked_keys () -{ - const bool json_block_l = request.get ("json_block", false); - auto count (count_optional_impl ()); - nano::uint256_union key (0); - boost::optional hash_text (request.get_optional ("key")); - if (!ec && hash_text.is_initialized ()) - { - if (key.decode_hex (hash_text.get ())) - { - ec = nano::error_rpc::bad_key; - } - } - if (!ec) - { - boost::property_tree::ptree unchecked; - auto transaction (node.store.tx_begin_read ()); - for (auto i (node.store.unchecked_begin (transaction, nano::unchecked_key (key, 0))), n (node.store.unchecked_end ()); i != n && unchecked.size () < count; ++i) - { - boost::property_tree::ptree entry; - nano::unchecked_info info (i->second); - entry.put ("key", nano::block_hash (i->first.key ()).to_string ()); - entry.put ("hash", info.block->hash ().to_string ()); - entry.put ("modified_timestamp", std::to_string (info.modified)); - if (json_block_l) - { - boost::property_tree::ptree block_node_l; - info.block->serialize_json (block_node_l); - entry.add_child ("contents", block_node_l); - } - else - { - std::string contents; - info.block->serialize_json (contents); - entry.put ("contents", contents); - } - unchecked.push_back (std::make_pair ("", entry)); - } - response_l.add_child ("unchecked", unchecked); - } - response_errors (); -} - -void nano::rpc_handler::unopened () -{ - rpc_control_impl (); - if (!ec) - { - auto count (count_optional_impl ()); - nano::account start (1); // exclude burn account by default - boost::optional account_text (request.get_optional ("account")); - if (account_text.is_initialized ()) - { - if (start.decode_account (account_text.get ())) - { - ec = nano::error_common::bad_account_number; - } - } - if (!ec) - { - auto transaction (node.store.tx_begin_read ()); - auto iterator (node.store.pending_begin (transaction, nano::pending_key (start, 0))); - auto end (node.store.pending_end ()); - nano::account current_account (start); - nano::uint128_t current_account_sum{ 0 }; - boost::property_tree::ptree accounts; - while (iterator != end && accounts.size () < count) - { - nano::pending_key key (iterator->first); - nano::account account (key.account); - nano::pending_info info (iterator->second); - if (node.store.account_exists (transaction, account)) - { - if (account.number () == std::numeric_limits::max ()) - { - break; - } - // Skip existing accounts - iterator = node.store.pending_begin (transaction, nano::pending_key (account.number () + 1, 0)); - } - else - { - if (account != current_account) - { - if (current_account_sum > 0) - { - accounts.put (current_account.to_account (), current_account_sum.convert_to ()); - current_account_sum = 0; - } - current_account = account; - } - current_account_sum += info.amount.number (); - ++iterator; - } - } - // last one after iterator reaches end - if (current_account_sum > 0 && accounts.size () < count) - { - accounts.put (current_account.to_account (), current_account_sum.convert_to ()); - } - response_l.add_child ("accounts", accounts); - } - } - response_errors (); -} - -void nano::rpc_handler::uptime () -{ - response_l.put ("seconds", std::chrono::duration_cast (std::chrono::steady_clock::now () - node.startup_time).count ()); - response_errors (); -} - -void nano::rpc_handler::version () -{ - response_l.put ("rpc_version", "1"); - response_l.put ("store_version", std::to_string (node.store_version ())); - response_l.put ("protocol_version", std::to_string (nano::protocol_version)); - if (NANO_VERSION_PATCH == 0) - { - response_l.put ("node_vendor", boost::str (boost::format ("Nano %1%") % NANO_MAJOR_MINOR_VERSION)); - } - else - { - response_l.put ("node_vendor", boost::str (boost::format ("Nano %1%") % NANO_MAJOR_MINOR_RC_VERSION)); - } - response_errors (); -} - -void nano::rpc_handler::validate_account_number () -{ - std::string account_text (request.get ("account")); - nano::uint256_union account; - auto error (account.decode_account (account_text)); - response_l.put ("valid", error ? "0" : "1"); - response_errors (); -} - -void nano::rpc_handler::wallet_add () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - std::string key_text (request.get ("key")); - nano::raw_key key; - if (!key.data.decode_hex (key_text)) - { - const bool generate_work = request.get ("work", true); - auto pub (wallet->insert_adhoc (key, generate_work)); - if (!pub.is_zero ()) - { - response_l.put ("account", pub.to_account ()); - } - else - { - ec = nano::error_common::wallet_locked; - } - } - else - { - ec = nano::error_common::bad_private_key; - } - } - response_errors (); -} - -void nano::rpc_handler::wallet_add_watch () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_write ()); - if (wallet->store.valid_password (transaction)) - { - for (auto & accounts : request.get_child ("accounts")) - { - auto account (account_impl (accounts.second.data ())); - if (!ec) - { - wallet->insert_watch (transaction, account); - } - } - response_l.put ("success", ""); - } - else - { - ec = nano::error_common::wallet_locked; - } - } - response_errors (); -} - -void nano::rpc_handler::wallet_info () -{ - auto wallet (wallet_impl ()); - if (!ec) - { - nano::uint128_t balance (0); - nano::uint128_t pending (0); - uint64_t count (0); - uint64_t deterministic_count (0); - uint64_t adhoc_count (0); - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) - { - nano::account account (i->first); - balance = balance + node.ledger.account_balance (block_transaction, account); - pending = pending + node.ledger.account_pending (block_transaction, account); - nano::key_type key_type (wallet->store.key_type (i->second)); - if (key_type == nano::key_type::deterministic) - { - deterministic_count++; - } - else if (key_type == nano::key_type::adhoc) - { - adhoc_count++; - } - count++; - } - uint32_t deterministic_index (wallet->store.deterministic_index_get (transaction)); - response_l.put ("balance", balance.convert_to ()); - response_l.put ("pending", pending.convert_to ()); - response_l.put ("accounts_count", std::to_string (count)); - response_l.put ("deterministic_count", std::to_string (deterministic_count)); - response_l.put ("adhoc_count", std::to_string (adhoc_count)); - response_l.put ("deterministic_index", std::to_string (deterministic_index)); - } - response_errors (); -} - -void nano::rpc_handler::wallet_balances () -{ - auto wallet (wallet_impl ()); - auto threshold (threshold_optional_impl ()); - if (!ec) - { - boost::property_tree::ptree balances; - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) - { - nano::account account (i->first); - nano::uint128_t balance = node.ledger.account_balance (block_transaction, account); - if (balance >= threshold.number ()) - { - boost::property_tree::ptree entry; - nano::uint128_t pending = node.ledger.account_pending (block_transaction, account); - entry.put ("balance", balance.convert_to ()); - entry.put ("pending", pending.convert_to ()); - balances.push_back (std::make_pair (account.to_account (), entry)); - } - } - response_l.add_child ("balances", balances); - } - response_errors (); -} - -void nano::rpc_handler::wallet_change_seed () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - std::string seed_text (request.get ("seed")); - nano::raw_key seed; - if (!seed.data.decode_hex (seed_text)) - { - auto count (static_cast (count_optional_impl (0))); - auto transaction (node.wallets.tx_begin_write ()); - if (wallet->store.valid_password (transaction)) - { - nano::public_key account (wallet->change_seed (transaction, seed, count)); - response_l.put ("success", ""); - response_l.put ("last_restored_account", account.to_account ()); - auto index (wallet->store.deterministic_index_get (transaction)); - assert (index > 0); - response_l.put ("restored_count", std::to_string (index)); - } - else - { - ec = nano::error_common::wallet_locked; - } - } - else - { - ec = nano::error_common::bad_seed; - } - } - response_errors (); -} - -void nano::rpc_handler::wallet_contains () -{ - auto account (account_impl ()); - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - auto exists (wallet->store.find (transaction, account) != wallet->store.end ()); - response_l.put ("exists", exists ? "1" : "0"); - } - response_errors (); -} - -void nano::rpc_handler::wallet_create () -{ - rpc_control_impl (); - if (!ec) - { - nano::raw_key seed; - auto seed_text (request.get_optional ("seed")); - if (seed_text.is_initialized () && seed.data.decode_hex (seed_text.get ())) - { - ec = nano::error_common::bad_seed; - } - if (!ec) - { - nano::keypair wallet_id; - auto wallet (node.wallets.create (wallet_id.pub)); - auto existing (node.wallets.items.find (wallet_id.pub)); - if (existing != node.wallets.items.end ()) - { - response_l.put ("wallet", wallet_id.pub.to_string ()); - } - else - { - ec = nano::error_common::wallet_lmdb_max_dbs; - } - if (!ec && seed_text.is_initialized ()) - { - auto transaction (node.wallets.tx_begin_write ()); - nano::public_key account (wallet->change_seed (transaction, seed)); - response_l.put ("last_restored_account", account.to_account ()); - auto index (wallet->store.deterministic_index_get (transaction)); - assert (index > 0); - response_l.put ("restored_count", std::to_string (index)); - } - } - } - response_errors (); -} - -void nano::rpc_handler::wallet_destroy () -{ - rpc_control_impl (); - if (!ec) - { - std::string wallet_text (request.get ("wallet")); - nano::uint256_union wallet; - if (!wallet.decode_hex (wallet_text)) - { - auto existing (node.wallets.items.find (wallet)); - if (existing != node.wallets.items.end ()) - { - node.wallets.destroy (wallet); - bool destroyed (node.wallets.items.find (wallet) == node.wallets.items.end ()); - response_l.put ("destroyed", destroyed ? "1" : "0"); - } - else - { - ec = nano::error_common::wallet_not_found; - } - } - else - { - ec = nano::error_common::bad_wallet_number; - } - } - response_errors (); -} - -void nano::rpc_handler::wallet_export () -{ - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - std::string json; - wallet->store.serialize_json (transaction, json); - response_l.put ("json", json); - } - response_errors (); -} - -void nano::rpc_handler::wallet_frontiers () -{ - auto wallet (wallet_impl ()); - if (!ec) - { - boost::property_tree::ptree frontiers; - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) - { - nano::account account (i->first); - auto latest (node.ledger.latest (block_transaction, account)); - if (!latest.is_zero ()) - { - frontiers.put (account.to_account (), latest.to_string ()); - } - } - response_l.add_child ("frontiers", frontiers); - } - response_errors (); -} - -void nano::rpc_handler::wallet_history () -{ - uint64_t modified_since (1); - boost::optional modified_since_text (request.get_optional ("modified_since")); - if (modified_since_text.is_initialized ()) - { - if (decode_unsigned (modified_since_text.get (), modified_since)) - { - ec = nano::error_rpc::invalid_timestamp; - } - } - auto wallet (wallet_impl ()); - if (!ec) - { - std::multimap> entries; - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) - { - nano::account account (i->first); - nano::account_info info; - if (!node.store.account_get (block_transaction, account, info)) - { - auto timestamp (info.modified); - auto hash (info.head); - while (timestamp >= modified_since && !hash.is_zero ()) - { - nano::block_sideband sideband; - auto block (node.store.block_get (block_transaction, hash, &sideband)); - timestamp = sideband.timestamp; - if (block != nullptr && timestamp >= modified_since) - { - boost::property_tree::ptree entry; - std::vector no_filter; - history_visitor visitor (*this, false, block_transaction, entry, hash, no_filter); - block->visit (visitor); - if (!entry.empty ()) - { - entry.put ("block_account", account.to_account ()); - entry.put ("hash", hash.to_string ()); - entry.put ("local_timestamp", std::to_string (timestamp)); - entries.insert (std::make_pair (timestamp, entry)); - } - hash = block->previous (); - } - else - { - hash.clear (); - } - } - } - } - boost::property_tree::ptree history; - for (auto i (entries.begin ()), n (entries.end ()); i != n; ++i) - { - history.push_back (std::make_pair ("", i->second)); - } - response_l.add_child ("history", history); - } - response_errors (); -} - -void nano::rpc_handler::wallet_key_valid () -{ - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - auto valid (wallet->store.valid_password (transaction)); - response_l.put ("valid", valid ? "1" : "0"); - } - response_errors (); -} - -void nano::rpc_handler::wallet_ledger () -{ - const bool representative = request.get ("representative", false); - const bool weight = request.get ("weight", false); - const bool pending = request.get ("pending", false); - uint64_t modified_since (0); - boost::optional modified_since_text (request.get_optional ("modified_since")); - if (modified_since_text.is_initialized ()) - { - modified_since = strtoul (modified_since_text.get ().c_str (), NULL, 10); - } - auto wallet (wallet_impl ()); - if (!ec) - { - boost::property_tree::ptree accounts; - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) - { - nano::account account (i->first); - nano::account_info info; - if (!node.store.account_get (block_transaction, account, info)) - { - if (info.modified >= modified_since) - { - boost::property_tree::ptree entry; - entry.put ("frontier", info.head.to_string ()); - entry.put ("open_block", info.open_block.to_string ()); - entry.put ("representative_block", info.rep_block.to_string ()); - std::string balance; - nano::uint128_union (info.balance).encode_dec (balance); - entry.put ("balance", balance); - entry.put ("modified_timestamp", std::to_string (info.modified)); - entry.put ("block_count", std::to_string (info.block_count)); - if (representative) - { - auto block (node.store.block_get (block_transaction, info.rep_block)); - assert (block != nullptr); - entry.put ("representative", block->representative ().to_account ()); - } - if (weight) - { - auto account_weight (node.ledger.weight (block_transaction, account)); - entry.put ("weight", account_weight.convert_to ()); - } - if (pending) - { - auto account_pending (node.ledger.account_pending (block_transaction, account)); - entry.put ("pending", account_pending.convert_to ()); - } - accounts.push_back (std::make_pair (account.to_account (), entry)); - } - } - } - response_l.add_child ("accounts", accounts); - } - response_errors (); -} - -void nano::rpc_handler::wallet_lock () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - nano::raw_key empty; - empty.data.clear (); - wallet->store.password.value_set (empty); - response_l.put ("locked", "1"); - node.logger.try_log ("Wallet locked"); - } - response_errors (); -} - -void nano::rpc_handler::wallet_pending () -{ - auto wallet (wallet_impl ()); - auto count (count_optional_impl ()); - auto threshold (threshold_optional_impl ()); - const bool source = request.get ("source", false); - const bool min_version = request.get ("min_version", false); - const bool include_active = request.get ("include_active", false); - if (!ec) - { - boost::property_tree::ptree pending; - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) - { - nano::account account (i->first); - boost::property_tree::ptree peers_l; - for (auto ii (node.store.pending_begin (block_transaction, nano::pending_key (account, 0))); nano::pending_key (ii->first).account == account && peers_l.size () < count; ++ii) - { - nano::pending_key key (ii->first); - if (include_active || node.ledger.block_confirmed (block_transaction, key.hash)) - { - if (threshold.is_zero () && !source) - { - boost::property_tree::ptree entry; - entry.put ("", key.hash.to_string ()); - peers_l.push_back (std::make_pair ("", entry)); - } - else - { - nano::pending_info info (ii->second); - if (info.amount.number () >= threshold.number ()) - { - if (source || min_version) - { - boost::property_tree::ptree pending_tree; - pending_tree.put ("amount", info.amount.number ().convert_to ()); - if (source) - { - pending_tree.put ("source", info.source.to_account ()); - } - if (min_version) - { - pending_tree.put ("min_version", info.epoch == nano::epoch::epoch_1 ? "1" : "0"); - } - peers_l.add_child (key.hash.to_string (), pending_tree); - } - else - { - peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); - } - } - } - } - } - if (!peers_l.empty ()) - { - pending.add_child (account.to_account (), peers_l); - } - } - response_l.add_child ("blocks", pending); - } - response_errors (); -} - -void nano::rpc_handler::wallet_representative () -{ - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - response_l.put ("representative", wallet->store.representative (transaction).to_account ()); - } - response_errors (); -} - -void nano::rpc_handler::wallet_representative_set () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - std::string representative_text (request.get ("representative")); - nano::account representative; - if (!representative.decode_account (representative_text)) - { - bool update_existing_accounts (request.get ("update_existing_accounts", false)); - { - auto transaction (node.wallets.tx_begin_write ()); - if (wallet->store.valid_password (transaction) || !update_existing_accounts) - { - wallet->store.representative_set (transaction, representative); - response_l.put ("set", "1"); - } - else - { - ec = nano::error_common::wallet_locked; - } - } - // Change representative for all wallet accounts - if (!ec && update_existing_accounts) - { - std::vector accounts; - { - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) - { - nano::account account (i->first); - nano::account_info info; - if (!node.store.account_get (block_transaction, account, info)) - { - auto block (node.store.block_get (block_transaction, info.rep_block)); - assert (block != nullptr); - if (block->representative () != representative) - { - accounts.push_back (account); - } - } - } - } - for (auto & account : accounts) - { - // clang-format off - wallet->change_async (account, representative, [](std::shared_ptr) {}, 0, false); - // clang-format on - } - } - } - else - { - ec = nano::error_rpc::bad_representative_number; - } - } - response_errors (); -} - -void nano::rpc_handler::wallet_republish () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - auto count (count_impl ()); - if (!ec) - { - boost::property_tree::ptree blocks; - std::deque> republish_bundle; - auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); - for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) - { - nano::account account (i->first); - auto latest (node.ledger.latest (block_transaction, account)); - std::shared_ptr block; - std::vector hashes; - while (!latest.is_zero () && hashes.size () < count) - { - hashes.push_back (latest); - block = node.store.block_get (block_transaction, latest); - latest = block->previous (); - } - std::reverse (hashes.begin (), hashes.end ()); - for (auto & hash : hashes) - { - block = node.store.block_get (block_transaction, hash); - republish_bundle.push_back (std::move (block)); - boost::property_tree::ptree entry; - entry.put ("", hash.to_string ()); - blocks.push_back (std::make_pair ("", entry)); - } - } - node.network.flood_block_batch (republish_bundle, 25); - response_l.add_child ("blocks", blocks); - } - response_errors (); -} - -void nano::rpc_handler::wallet_seed () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - if (wallet->store.valid_password (transaction)) - { - nano::raw_key seed; - wallet->store.seed (seed, transaction); - response_l.put ("seed", seed.data.to_string ()); - } - else - { - ec = nano::error_common::wallet_locked; - } - } - response_errors (); -} - -void nano::rpc_handler::wallet_work_get () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - if (!ec) - { - boost::property_tree::ptree works; - auto transaction (node.wallets.tx_begin_read ()); - for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) - { - nano::account account (i->first); - uint64_t work (0); - auto error_work (wallet->store.work_get (transaction, account, work)); - (void)error_work; - works.put (account.to_account (), nano::to_string_hex (work)); - } - response_l.add_child ("works", works); - } - response_errors (); -} - -void nano::rpc_handler::work_generate () -{ - rpc_control_impl (); - auto hash (hash_impl ()); - uint64_t difficulty (node.network_params.network.publish_threshold); - boost::optional difficulty_text (request.get_optional ("difficulty")); - if (!ec && difficulty_text.is_initialized ()) - { - if (nano::from_string_hex (difficulty_text.get (), difficulty)) - { - ec = nano::error_rpc::bad_difficulty_format; - } - } - if (!ec && difficulty > rpc.config.max_work_generate_difficulty) - { - ec = nano::error_rpc::difficulty_limit; - } - if (!ec) - { - bool use_peers (request.get_optional ("use_peers") == true); - auto rpc_l (shared_from_this ()); - auto callback = [rpc_l](boost::optional const & work_a) { - if (work_a) - { - boost::property_tree::ptree response_l; - response_l.put ("work", nano::to_string_hex (work_a.value ())); - rpc_l->response (response_l); - } - else - { - error_response (rpc_l->response, "Cancelled"); - } - }; - if (!use_peers) - { - node.work.generate (hash, callback, difficulty); - } - else - { - node.work_generate (hash, callback, difficulty); - } - } - // Because of callback - if (ec) - { - response_errors (); - } -} - -void nano::rpc_handler::work_cancel () -{ - rpc_control_impl (); - auto hash (hash_impl ()); - if (!ec) - { - node.work.cancel (hash); - } - response_errors (); -} - -void nano::rpc_handler::work_get () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - auto account (account_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_read ()); - wallet_account_impl (transaction, wallet, account); - if (!ec) - { - uint64_t work (0); - auto error_work (wallet->store.work_get (transaction, account, work)); - (void)error_work; - response_l.put ("work", nano::to_string_hex (work)); - } - } - response_errors (); -} - -void nano::rpc_handler::work_set () -{ - rpc_control_impl (); - auto wallet (wallet_impl ()); - auto account (account_impl ()); - auto work (work_optional_impl ()); - if (!ec) - { - auto transaction (node.wallets.tx_begin_write ()); - wallet_account_impl (transaction, wallet, account); - if (!ec) - { - wallet->store.work_put (transaction, account, work); - response_l.put ("success", ""); - } - } - response_errors (); -} - -void nano::rpc_handler::work_validate () -{ - auto hash (hash_impl ()); - auto work (work_optional_impl ()); - uint64_t difficulty (node.network_params.network.publish_threshold); - boost::optional difficulty_text (request.get_optional ("difficulty")); - if (!ec && difficulty_text.is_initialized ()) - { - if (nano::from_string_hex (difficulty_text.get (), difficulty)) - { - ec = nano::error_rpc::bad_difficulty_format; - } - } - if (!ec) - { - uint64_t result_difficulty (0); - bool invalid (nano::work_validate (hash, work, &result_difficulty)); - bool valid (!invalid && result_difficulty >= difficulty); - response_l.put ("valid", valid ? "1" : "0"); - } - response_errors (); -} - -void nano::rpc_handler::work_peer_add () -{ - rpc_control_impl (); - if (!ec) - { - std::string address_text = request.get ("address"); - std::string port_text = request.get ("port"); - uint16_t port; - if (!nano::parse_port (port_text, port)) - { - node.config.work_peers.push_back (std::make_pair (address_text, port)); - response_l.put ("success", ""); - } - else - { - ec = nano::error_common::invalid_port; - } - } - response_errors (); -} - -void nano::rpc_handler::work_peers () -{ - rpc_control_impl (); - if (!ec) - { - boost::property_tree::ptree work_peers_l; - for (auto i (node.config.work_peers.begin ()), n (node.config.work_peers.end ()); i != n; ++i) - { - boost::property_tree::ptree entry; - entry.put ("", boost::str (boost::format ("%1%:%2%") % i->first % i->second)); - work_peers_l.push_back (std::make_pair ("", entry)); - } - response_l.add_child ("work_peers", work_peers_l); - } - response_errors (); -} - -void nano::rpc_handler::work_peers_clear () -{ - rpc_control_impl (); - if (!ec) - { - node.config.work_peers.clear (); - response_l.put ("success", ""); - } - response_errors (); -} - -namespace -{ std::string filter_request (boost::property_tree::ptree tree_a) { // Replace password @@ -4414,265 +186,3 @@ std::string filter_request (boost::property_tree::ptree tree_a) return result; } } - -void nano::rpc_handler::process_request (bool unsafe_a) -{ - try - { - auto max_depth_exceeded (false); - auto max_depth_possible (0); - for (auto ch : body) - { - if (ch == '[' || ch == '{') - { - if (max_depth_possible >= rpc.config.max_json_depth) - { - max_depth_exceeded = true; - break; - } - ++max_depth_possible; - } - } - if (max_depth_exceeded) - { - error_response (response, "Max JSON depth exceeded"); - } - else - { - std::stringstream istream (body); - boost::property_tree::read_json (istream, request); - std::string action (request.get ("action")); - if (node.config.logging.log_rpc ()) - { - rpc.node.logger.always_log (boost::str (boost::format ("%1% ") % request_id), filter_request (request)); - } - - auto no_arg_func_iter = rpc_handler_no_arg_funcs.find (action); - if (no_arg_func_iter != rpc_handler_no_arg_funcs.cend ()) - { - // First try the map of options with no arguments - no_arg_func_iter->second (this); - } - else - { - // Try the rest of the options - if (action == "wallet_seed") - { - if (unsafe_a || rpc.node.network_params.network.is_test_network ()) - { - wallet_seed (); - } - else - { - error_response (response, "Unsafe RPC not allowed"); - } - } - else if (action == "chain") - { - chain (); - } - else if (action == "successors") - { - chain (true); - } - else if (action == "history") - { - request.put ("head", request.get ("hash")); - account_history (); - } - else if (action == "knano_from_raw" || action == "krai_from_raw") - { - mnano_from_raw (nano::kxrb_ratio); - } - else if (action == "knano_to_raw" || action == "krai_to_raw") - { - mnano_to_raw (nano::kxrb_ratio); - } - else if (action == "nano_from_raw" || action == "rai_from_raw") - { - mnano_from_raw (nano::xrb_ratio); - } - else if (action == "nano_to_raw" || action == "rai_to_raw") - { - mnano_to_raw (nano::xrb_ratio); - } - else if (action == "mnano_from_raw" || action == "mrai_from_raw") - { - mnano_from_raw (); - } - else if (action == "mnano_to_raw" || action == "mrai_to_raw") - { - mnano_to_raw (); - } - else if (action == "password_valid") - { - password_valid (); - } - else if (action == "wallet_locked") - { - password_valid (true); - } - else - { - error_response (response, "Unknown command"); - } - } - } - } - catch (std::runtime_error const &) - { - error_response (response, "Unable to parse JSON"); - } - catch (...) - { - error_response (response, "Internal server error in RPC"); - } -} - -void nano::error_response (std::function response_a, std::string const & message_a) -{ - boost::property_tree::ptree response_l; - response_l.put ("error", message_a); - response_a (response_l); -} - -namespace -{ -rpc_handler_no_arg_func_map create_rpc_handler_no_arg_func_map () -{ - rpc_handler_no_arg_func_map no_arg_funcs; - no_arg_funcs.emplace ("account_balance", &nano::rpc_handler::account_balance); - no_arg_funcs.emplace ("account_block_count", &nano::rpc_handler::account_block_count); - no_arg_funcs.emplace ("account_count", &nano::rpc_handler::account_count); - no_arg_funcs.emplace ("account_create", &nano::rpc_handler::account_create); - no_arg_funcs.emplace ("account_get", &nano::rpc_handler::account_get); - no_arg_funcs.emplace ("account_history", &nano::rpc_handler::account_history); - no_arg_funcs.emplace ("account_info", &nano::rpc_handler::account_info); - no_arg_funcs.emplace ("account_key", &nano::rpc_handler::account_key); - no_arg_funcs.emplace ("account_list", &nano::rpc_handler::account_list); - no_arg_funcs.emplace ("account_move", &nano::rpc_handler::account_move); - no_arg_funcs.emplace ("account_remove", &nano::rpc_handler::account_remove); - no_arg_funcs.emplace ("account_representative", &nano::rpc_handler::account_representative); - no_arg_funcs.emplace ("account_representative_set", &nano::rpc_handler::account_representative_set); - no_arg_funcs.emplace ("account_weight", &nano::rpc_handler::account_weight); - no_arg_funcs.emplace ("accounts_balances", &nano::rpc_handler::accounts_balances); - no_arg_funcs.emplace ("accounts_create", &nano::rpc_handler::accounts_create); - no_arg_funcs.emplace ("accounts_frontiers", &nano::rpc_handler::accounts_frontiers); - no_arg_funcs.emplace ("accounts_pending", &nano::rpc_handler::accounts_pending); - no_arg_funcs.emplace ("available_supply", &nano::rpc_handler::available_supply); - no_arg_funcs.emplace ("block_info", &nano::rpc_handler::block_info); - no_arg_funcs.emplace ("block", &nano::rpc_handler::block_info); - no_arg_funcs.emplace ("block_confirm", &nano::rpc_handler::block_confirm); - no_arg_funcs.emplace ("blocks", &nano::rpc_handler::blocks); - no_arg_funcs.emplace ("blocks_info", &nano::rpc_handler::blocks_info); - no_arg_funcs.emplace ("block_account", &nano::rpc_handler::block_account); - no_arg_funcs.emplace ("block_count", &nano::rpc_handler::block_count); - no_arg_funcs.emplace ("block_count_type", &nano::rpc_handler::block_count_type); - no_arg_funcs.emplace ("block_create", &nano::rpc_handler::block_create); - no_arg_funcs.emplace ("block_hash", &nano::rpc_handler::block_hash); - no_arg_funcs.emplace ("bootstrap", &nano::rpc_handler::bootstrap); - no_arg_funcs.emplace ("bootstrap_any", &nano::rpc_handler::bootstrap_any); - no_arg_funcs.emplace ("bootstrap_lazy", &nano::rpc_handler::bootstrap_lazy); - no_arg_funcs.emplace ("bootstrap_status", &nano::rpc_handler::bootstrap_status); - no_arg_funcs.emplace ("delegators", &nano::rpc_handler::delegators); - no_arg_funcs.emplace ("delegators_count", &nano::rpc_handler::delegators_count); - no_arg_funcs.emplace ("deterministic_key", &nano::rpc_handler::deterministic_key); - no_arg_funcs.emplace ("confirmation_active", &nano::rpc_handler::confirmation_active); - no_arg_funcs.emplace ("confirmation_history", &nano::rpc_handler::confirmation_history); - no_arg_funcs.emplace ("confirmation_info", &nano::rpc_handler::confirmation_info); - no_arg_funcs.emplace ("confirmation_quorum", &nano::rpc_handler::confirmation_quorum); - no_arg_funcs.emplace ("frontiers", &nano::rpc_handler::frontiers); - no_arg_funcs.emplace ("frontier_count", &nano::rpc_handler::account_count); - no_arg_funcs.emplace ("keepalive", &nano::rpc_handler::keepalive); - no_arg_funcs.emplace ("key_create", &nano::rpc_handler::key_create); - no_arg_funcs.emplace ("key_expand", &nano::rpc_handler::key_expand); - no_arg_funcs.emplace ("ledger", &nano::rpc_handler::ledger); - no_arg_funcs.emplace ("node_id", &nano::rpc_handler::node_id); - no_arg_funcs.emplace ("node_id_delete", &nano::rpc_handler::node_id_delete); - no_arg_funcs.emplace ("password_change", &nano::rpc_handler::password_change); - no_arg_funcs.emplace ("password_enter", &nano::rpc_handler::password_enter); - no_arg_funcs.emplace ("wallet_unlock", &nano::rpc_handler::password_enter); - no_arg_funcs.emplace ("payment_begin", &nano::rpc_handler::payment_begin); - no_arg_funcs.emplace ("payment_init", &nano::rpc_handler::payment_init); - no_arg_funcs.emplace ("payment_end", &nano::rpc_handler::payment_end); - no_arg_funcs.emplace ("payment_wait", &nano::rpc_handler::payment_wait); - no_arg_funcs.emplace ("peers", &nano::rpc_handler::peers); - no_arg_funcs.emplace ("pending", &nano::rpc_handler::pending); - no_arg_funcs.emplace ("pending_exists", &nano::rpc_handler::pending_exists); - no_arg_funcs.emplace ("process", &nano::rpc_handler::process); - no_arg_funcs.emplace ("receive", &nano::rpc_handler::receive); - no_arg_funcs.emplace ("receive_minimum", &nano::rpc_handler::receive_minimum); - no_arg_funcs.emplace ("receive_minimum_set", &nano::rpc_handler::receive_minimum_set); - no_arg_funcs.emplace ("representatives", &nano::rpc_handler::representatives); - no_arg_funcs.emplace ("representatives_online", &nano::rpc_handler::representatives_online); - no_arg_funcs.emplace ("republish", &nano::rpc_handler::republish); - no_arg_funcs.emplace ("search_pending", &nano::rpc_handler::search_pending); - no_arg_funcs.emplace ("search_pending_all", &nano::rpc_handler::search_pending_all); - no_arg_funcs.emplace ("send", &nano::rpc_handler::send); - no_arg_funcs.emplace ("sign", &nano::rpc_handler::sign); - no_arg_funcs.emplace ("stats", &nano::rpc_handler::stats); - no_arg_funcs.emplace ("stats_clear", &nano::rpc_handler::stats_clear); - no_arg_funcs.emplace ("stop", &nano::rpc_handler::stop); - no_arg_funcs.emplace ("unchecked", &nano::rpc_handler::unchecked); - no_arg_funcs.emplace ("unchecked_clear", &nano::rpc_handler::unchecked_clear); - no_arg_funcs.emplace ("unchecked_get", &nano::rpc_handler::unchecked_get); - no_arg_funcs.emplace ("unchecked_keys", &nano::rpc_handler::unchecked_keys); - no_arg_funcs.emplace ("unopened", &nano::rpc_handler::unopened); - no_arg_funcs.emplace ("uptime", &nano::rpc_handler::uptime); - no_arg_funcs.emplace ("validate_account_number", &nano::rpc_handler::validate_account_number); - no_arg_funcs.emplace ("version", &nano::rpc_handler::version); - no_arg_funcs.emplace ("wallet_add", &nano::rpc_handler::wallet_add); - no_arg_funcs.emplace ("wallet_add_watch", &nano::rpc_handler::wallet_add_watch); - no_arg_funcs.emplace ("wallet_balances", &nano::rpc_handler::wallet_balances); - no_arg_funcs.emplace ("wallet_change_seed", &nano::rpc_handler::wallet_change_seed); - no_arg_funcs.emplace ("wallet_contains", &nano::rpc_handler::wallet_contains); - no_arg_funcs.emplace ("wallet_create", &nano::rpc_handler::wallet_create); - no_arg_funcs.emplace ("wallet_destroy", &nano::rpc_handler::wallet_destroy); - no_arg_funcs.emplace ("wallet_export", &nano::rpc_handler::wallet_export); - no_arg_funcs.emplace ("wallet_frontiers", &nano::rpc_handler::wallet_frontiers); - no_arg_funcs.emplace ("wallet_history", &nano::rpc_handler::wallet_history); - no_arg_funcs.emplace ("wallet_info", &nano::rpc_handler::wallet_info); - no_arg_funcs.emplace ("wallet_balance_total", &nano::rpc_handler::wallet_info); - no_arg_funcs.emplace ("wallet_key_valid", &nano::rpc_handler::wallet_key_valid); - no_arg_funcs.emplace ("wallet_ledger", &nano::rpc_handler::wallet_ledger); - no_arg_funcs.emplace ("wallet_lock", &nano::rpc_handler::wallet_lock); - no_arg_funcs.emplace ("wallet_pending", &nano::rpc_handler::wallet_pending); - no_arg_funcs.emplace ("wallet_representative", &nano::rpc_handler::wallet_representative); - no_arg_funcs.emplace ("wallet_representative_set", &nano::rpc_handler::wallet_representative_set); - no_arg_funcs.emplace ("wallet_republish", &nano::rpc_handler::wallet_republish); - no_arg_funcs.emplace ("wallet_work_get", &nano::rpc_handler::wallet_work_get); - no_arg_funcs.emplace ("work_generate", &nano::rpc_handler::work_generate); - no_arg_funcs.emplace ("work_cancel", &nano::rpc_handler::work_cancel); - no_arg_funcs.emplace ("work_get", &nano::rpc_handler::work_get); - no_arg_funcs.emplace ("work_set", &nano::rpc_handler::work_set); - no_arg_funcs.emplace ("work_validate", &nano::rpc_handler::work_validate); - no_arg_funcs.emplace ("work_peer_add", &nano::rpc_handler::work_peer_add); - no_arg_funcs.emplace ("work_peers", &nano::rpc_handler::work_peers); - no_arg_funcs.emplace ("work_peers_clear", &nano::rpc_handler::work_peers_clear); - return no_arg_funcs; -} - -void construct_json (nano::seq_con_info_component * component, boost::property_tree::ptree & parent) -{ - // We are a leaf node, print name and exit - if (!component->is_composite ()) - { - auto & leaf_info = static_cast (component)->get_info (); - boost::property_tree::ptree child; - child.put ("count", leaf_info.count); - child.put ("size", leaf_info.count * leaf_info.sizeof_element); - parent.add_child (leaf_info.name, child); - return; - } - - auto composite = static_cast (component); - - boost::property_tree::ptree current; - for (auto & child : composite->get_children ()) - { - construct_json (child.get (), current); - } - - parent.add_child (composite->get_name (), current); -} -} diff --git a/nano/rpc/rpc_handler.hpp b/nano/rpc/rpc_handler.hpp index 2e252856..70e59df1 100644 --- a/nano/rpc/rpc_handler.hpp +++ b/nano/rpc/rpc_handler.hpp @@ -2,158 +2,28 @@ #include #include -#include -#include #include namespace nano { -class node; -class rpc; -void error_response (std::function 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 { public: - rpc_handler (nano::node &, nano::rpc &, std::string const &, std::string const &, std::function const &); + rpc_handler (nano::rpc_config const & rpc_config, std::string const & body_a, std::string const & request_id_a, std::function const & response_a, nano::rpc_handler_interface & rpc_handler_interface_a, nano::logger_mt & logger); + void process_request (); + void read (std::shared_ptr> req, std::shared_ptr> 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 response; - void response_errors (); - std::error_code ec; - boost::property_tree::ptree response_l; - std::shared_ptr wallet_impl (); - bool wallet_locked_impl (nano::transaction const &, std::shared_ptr); - bool wallet_account_impl (nano::transaction const &, std::shared_ptr, nano::account const &); - nano::account account_impl (std::string = ""); - nano::amount amount_impl (); - std::shared_ptr block_impl (bool = true); - std::shared_ptr 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::max ()); - uint64_t offset_optional_impl (uint64_t = 0); - bool rpc_control_impl (); + std::function response; + nano::rpc_config const & rpc_config; + nano::rpc_handler_interface & rpc_handler_interface; + nano::logger_mt & logger; }; } diff --git a/nano/rpc/rpc_request_processor.cpp b/nano/rpc/rpc_request_processor.cpp new file mode 100644 index 00000000..7788daa5 --- /dev/null +++ b/nano/rpc/rpc_request_processor.cpp @@ -0,0 +1,178 @@ +#include +#include + +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 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::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 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 lock (request_mutex); + stopped = true; + } + condition.notify_one (); + if (thread.joinable ()) + { + thread.join (); + } +} + +void nano::rpc_request_processor::add (std::shared_ptr request) +{ + { + std::lock_guard lk (request_mutex); + requests.push_back (request); + } + condition.notify_one (); +} + +void nano::rpc_request_processor::read_payload (std::shared_ptr connection, std::shared_ptr> res, std::shared_ptr rpc_request) +{ + uint32_t payload_size_l = boost::endian::big_to_native (*reinterpret_cast (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 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 connection, std::shared_ptr> req, std::shared_ptr> res, std::shared_ptr 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 lk (request_mutex); + while (!stopped) + { + if (!requests.empty ()) + { + lk.unlock (); + std::unique_lock 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> ()); + + // 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); + } + } +} diff --git a/nano/rpc/rpc_request_processor.hpp b/nano/rpc/rpc_request_processor.hpp new file mode 100644 index 00000000..70acbe88 --- /dev/null +++ b/nano/rpc/rpc_request_processor.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 response_a) : + action (action_a), body (body_a), response (response_a) + { + } + + std::string action; + std::string body; + std::function 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 request); + std::function stop_callback; + +private: + void run (); + void read_payload (std::shared_ptr connection, std::shared_ptr> res, std::shared_ptr rpc_request); + void try_reconnect_and_execute_request (std::shared_ptr connection, std::shared_ptr> req, std::shared_ptr> res, std::shared_ptr rpc_request); + void make_available (nano::ipc_connection & connection); + + std::vector> connections; + std::mutex request_mutex; + std::mutex connections_mutex; + bool stopped{ false }; + std::deque> 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 response_a) override + { + rpc_request_processor.add (std::make_shared (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; +}; +} diff --git a/nano/rpc/rpc_secure.cpp b/nano/rpc/rpc_secure.cpp index 151fcd4a..9e70acff 100644 --- a/nano/rpc/rpc_secure.cpp +++ b/nano/rpc/rpc_secure.cpp @@ -1,4 +1,5 @@ -#include +#include +#include #include #include @@ -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 (node, *this)); + auto connection (std::make_shared (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)); } }); } diff --git a/nano/rpc/rpc_secure.hpp b/nano/rpc/rpc_secure.hpp index 820fc3af..564c22b5 100644 --- a/nano/rpc/rpc_secure.hpp +++ b/nano/rpc/rpc_secure.hpp @@ -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; diff --git a/nano/rpc_test/CMakeLists.txt b/nano/rpc_test/CMakeLists.txt index 5a18de95..3c99ffd8 100644 --- a/nano/rpc_test/CMakeLists.txt +++ b/nano/rpc_test/CMakeLists.txt @@ -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 diff --git a/nano/rpc_test/entry.cpp b/nano/rpc_test/entry.cpp index ad34dda4..24a21a74 100644 --- a/nano/rpc_test/entry.cpp +++ b/nano/rpc_test/entry.cpp @@ -1,7 +1,7 @@ #include -#include 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; } diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 14f74f14..0d0d0ed1 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -1,27 +1,38 @@ -#include - -#include +#include #include -#include -#include -#include +#include #include -#include -#include +#include +#include +#include +#include +#include +#include #include #include +#include -using namespace std::chrono_literals; - +namespace +{ class test_response { public: - test_response (boost::property_tree::ptree const & request_a, nano::rpc & rpc_a, boost::asio::io_context & io_ctx) : + test_response (boost::property_tree::ptree const & request_a, boost::asio::io_context & io_ctx) : request (request_a), - sock (io_ctx), - status (0) + sock (io_ctx) { - sock.async_connect (nano::tcp_endpoint (boost::asio::ip::address_v6::loopback (), rpc_a.config.port), [this](boost::system::error_code const & ec) { + } + + test_response (boost::property_tree::ptree const & request_a, uint16_t port, boost::asio::io_context & io_ctx) : + request (request_a), + sock (io_ctx) + { + run (port); + } + + void run (uint16_t port) + { + sock.async_connect (nano::tcp_endpoint (boost::asio::ip::address_v6::loopback (), port), [this](boost::system::error_code const & ec) { if (!ec) { std::stringstream ostream; @@ -73,18 +84,36 @@ public: boost::beast::flat_buffer sb; boost::beast::http::request req; boost::beast::http::response resp; - int status; + std::atomic status{ 0 }; }; +void enable_ipc_transport_tcp (nano::ipc::ipc_config_tcp_socket & transport_tcp, uint16_t ipc_port) +{ + transport_tcp.enabled = true; + transport_tcp.port = ipc_port; +} + +void enable_ipc_transport_tcp (nano::ipc::ipc_config_tcp_socket & transport_tcp) +{ + static nano::network_constants network_constants; + enable_ipc_transport_tcp (transport_tcp, network_constants.default_ipc_port); +} +} + TEST (rpc, account_balance) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "account_balance"); request.put ("account", nano::test_genesis_key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -100,12 +129,17 @@ TEST (rpc, account_balance) TEST (rpc, account_block_count) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "account_block_count"); request.put ("account", nano::test_genesis_key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -119,12 +153,17 @@ TEST (rpc, account_block_count) TEST (rpc, account_create) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "account_create"); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); - test_response response0 (request, rpc, system.io_ctx); + test_response response0 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response0.status == 0) { @@ -137,7 +176,7 @@ TEST (rpc, account_create) ASSERT_TRUE (system.wallet (0)->exists (account0)); uint64_t max_index (std::numeric_limits::max ()); request.put ("index", max_index); - test_response response1 (request, rpc, system.io_ctx); + test_response response1 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -149,7 +188,7 @@ TEST (rpc, account_create) ASSERT_FALSE (account1.decode_account (account_text1)); ASSERT_TRUE (system.wallet (0)->exists (account1)); request.put ("index", max_index + 1); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -167,12 +206,16 @@ TEST (rpc, account_weight) auto & node1 (*system.nodes[0]); nano::change_block block (latest, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); ASSERT_EQ (nano::process_result::progress, node1.process (block).code); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "account_weight"); request.put ("account", key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -186,16 +229,21 @@ TEST (rpc, account_weight) TEST (rpc, wallet_contains) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); boost::property_tree::ptree request; std::string wallet; - system.nodes[0]->wallets.items.begin ()->first.encode_hex (wallet); + node->wallets.items.begin ()->first.encode_hex (wallet); request.put ("wallet", wallet); request.put ("action", "wallet_contains"); request.put ("account", nano::test_genesis_key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -209,15 +257,20 @@ TEST (rpc, wallet_contains) TEST (rpc, wallet_doesnt_contain) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; - system.nodes[0]->wallets.items.begin ()->first.encode_hex (wallet); + node->wallets.items.begin ()->first.encode_hex (wallet); request.put ("wallet", wallet); request.put ("action", "wallet_contains"); request.put ("account", nano::test_genesis_key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -231,13 +284,18 @@ TEST (rpc, wallet_doesnt_contain) TEST (rpc, validate_account_number) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); boost::property_tree::ptree request; request.put ("action", "validate_account_number"); request.put ("account", nano::test_genesis_key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -250,7 +308,12 @@ TEST (rpc, validate_account_number) TEST (rpc, validate_account_invalid) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); std::string account; nano::test_genesis_key.pub.encode_account (account); @@ -259,7 +322,7 @@ TEST (rpc, validate_account_invalid) boost::property_tree::ptree request; request.put ("action", "validate_account_number"); request.put ("account", account); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -273,7 +336,12 @@ TEST (rpc, validate_account_invalid) TEST (rpc, send) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); boost::property_tree::ptree request; @@ -291,7 +359,7 @@ TEST (rpc, send) ASSERT_NO_ERROR (system.poll ()); } }); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); while (response.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -300,19 +368,24 @@ TEST (rpc, send) std::string block_text (response.json.get ("block")); nano::block_hash block; ASSERT_FALSE (block.decode_hex (block_text)); - ASSERT_TRUE (system.nodes[0]->ledger.block_exists (block)); - ASSERT_EQ (system.nodes[0]->latest (nano::test_genesis_key.pub), block); + ASSERT_TRUE (node->ledger.block_exists (block)); + ASSERT_EQ (node->latest (nano::test_genesis_key.pub), block); thread2.join (); } TEST (rpc, send_fail) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; - system.nodes[0]->wallets.items.begin ()->first.encode_hex (wallet); + node->wallets.items.begin ()->first.encode_hex (wallet); request.put ("wallet", wallet); request.put ("action", "send"); request.put ("source", nano::test_genesis_key.pub.to_account ()); @@ -326,7 +399,7 @@ TEST (rpc, send_fail) ASSERT_NO_ERROR (system.poll ()); } }); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); while (response.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -339,7 +412,12 @@ TEST (rpc, send_fail) TEST (rpc, send_work) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); boost::property_tree::ptree request; @@ -351,7 +429,7 @@ TEST (rpc, send_work) request.put ("destination", nano::test_genesis_key.pub.to_account ()); request.put ("amount", "100"); request.put ("work", "1"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (10s); while (response.status == 0) { @@ -360,7 +438,7 @@ TEST (rpc, send_work) ASSERT_EQ (response.json.get ("error"), "Invalid work"); request.erase ("work"); request.put ("work", nano::to_string_hex (system.nodes[0]->work_generate_blocking (system.nodes[0]->latest (nano::test_genesis_key.pub)))); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); system.deadline_set (10s); while (response2.status == 0) { @@ -377,7 +455,12 @@ TEST (rpc, send_work) TEST (rpc, send_idempotent) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); boost::property_tree::ptree request; @@ -389,7 +472,7 @@ TEST (rpc, send_idempotent) request.put ("destination", nano::account (0).to_account ()); request.put ("amount", (nano::genesis_amount - (nano::genesis_amount / 4)).convert_to ()); request.put ("id", "123abc"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -401,7 +484,7 @@ TEST (rpc, send_idempotent) ASSERT_FALSE (block.decode_hex (block_text)); ASSERT_TRUE (system.nodes[0]->ledger.block_exists (block)); ASSERT_EQ (system.nodes[0]->balance (nano::test_genesis_key.pub), nano::genesis_amount / 4); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -413,7 +496,7 @@ TEST (rpc, send_idempotent) ASSERT_EQ (system.nodes[0]->balance (nano::test_genesis_key.pub), nano::genesis_amount / 4); request.erase ("id"); request.put ("id", "456def"); - test_response response3 (request, rpc, system.io_ctx); + test_response response3 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response3.status == 0) { @@ -426,11 +509,16 @@ TEST (rpc, send_idempotent) TEST (rpc, stop) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "stop"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -441,7 +529,12 @@ TEST (rpc, stop) TEST (rpc, wallet_add) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); nano::keypair key1; std::string key_text; @@ -452,7 +545,7 @@ TEST (rpc, wallet_add) request.put ("wallet", wallet); request.put ("action", "wallet_add"); request.put ("key", key_text); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -467,14 +560,19 @@ TEST (rpc, wallet_add) TEST (rpc, wallet_password_valid) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; system.nodes[0]->wallets.items.begin ()->first.encode_hex (wallet); request.put ("wallet", wallet); request.put ("action", "password_valid"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -488,7 +586,12 @@ TEST (rpc, wallet_password_valid) TEST (rpc, wallet_password_change) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; @@ -496,7 +599,7 @@ TEST (rpc, wallet_password_change) request.put ("wallet", wallet); request.put ("action", "password_change"); request.put ("password", "test"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -524,7 +627,12 @@ TEST (rpc, wallet_password_enter) ASSERT_NO_ERROR (system.poll ()); system.wallet (0)->store.password.value (password_l); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; @@ -532,7 +640,7 @@ TEST (rpc, wallet_password_enter) request.put ("wallet", wallet); request.put ("action", "password_enter"); request.put ("password", ""); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -546,14 +654,19 @@ TEST (rpc, wallet_password_enter) TEST (rpc, wallet_representative) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; system.nodes[0]->wallets.items.begin ()->first.encode_hex (wallet); request.put ("wallet", wallet); request.put ("action", "wallet_representative"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -567,7 +680,12 @@ TEST (rpc, wallet_representative) TEST (rpc, wallet_representative_set) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; @@ -576,7 +694,7 @@ TEST (rpc, wallet_representative_set) nano::keypair key; request.put ("action", "wallet_representative_set"); request.put ("representative", key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -590,7 +708,12 @@ TEST (rpc, wallet_representative_set) TEST (rpc, wallet_representative_set_force) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); rpc.start (); boost::property_tree::ptree request; @@ -601,7 +724,7 @@ TEST (rpc, wallet_representative_set_force) request.put ("action", "wallet_representative_set"); request.put ("representative", key.pub.to_account ()); request.put ("update_existing_accounts", true); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -630,7 +753,12 @@ TEST (rpc, wallet_representative_set_force) TEST (rpc, account_list) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); nano::keypair key2; system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); @@ -640,7 +768,7 @@ TEST (rpc, account_list) system.nodes[0]->wallets.items.begin ()->first.encode_hex (wallet); request.put ("wallet", wallet); request.put ("action", "account_list"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -666,7 +794,12 @@ TEST (rpc, account_list) TEST (rpc, wallet_key_valid) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); boost::property_tree::ptree request; @@ -674,7 +807,7 @@ TEST (rpc, wallet_key_valid) system.nodes[0]->wallets.items.begin ()->first.encode_hex (wallet); request.put ("wallet", wallet); request.put ("action", "wallet_key_valid"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -688,11 +821,16 @@ TEST (rpc, wallet_key_valid) TEST (rpc, wallet_create) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_create"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -712,12 +850,17 @@ TEST (rpc, wallet_create_seed) nano::raw_key prv; nano::deterministic_key (seed.pub, 0, prv.data); auto pub (nano::pub_key (prv.data)); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_create"); request.put ("seed", seed.pub.to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); while (response.status == 0) { system.poll (); @@ -745,13 +888,18 @@ TEST (rpc, wallet_create_seed) TEST (rpc, wallet_export) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); boost::property_tree::ptree request; request.put ("action", "wallet_export"); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -771,13 +919,18 @@ TEST (rpc, wallet_destroy) { nano::system system (24000, 1); auto wallet_id (system.nodes[0]->wallets.items.begin ()->first); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); boost::property_tree::ptree request; request.put ("action", "wallet_destroy"); request.put ("wallet", wallet_id.to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -791,7 +944,12 @@ TEST (rpc, account_move) { nano::system system (24000, 1); auto wallet_id (system.nodes[0]->wallets.items.begin ()->first); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); auto destination (system.wallet (0)); nano::keypair key; @@ -808,7 +966,7 @@ TEST (rpc, account_move) entry.put ("", key.pub.to_account ()); keys.push_back (std::make_pair ("", entry)); request.add_child ("accounts", keys); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -825,12 +983,17 @@ TEST (rpc, account_move) TEST (rpc, block) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "block"); request.put ("hash", system.nodes[0]->latest (nano::genesis_account).to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -845,13 +1008,18 @@ TEST (rpc, block) TEST (rpc, block_account) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); nano::genesis genesis; boost::property_tree::ptree request; request.put ("action", "block_account"); request.put ("hash", genesis.hash ().to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -872,13 +1040,18 @@ TEST (rpc, chain) ASSERT_FALSE (genesis.is_zero ()); auto block (system.wallet (0)->send_action (nano::test_genesis_key.pub, key.pub, 1)); ASSERT_NE (nullptr, block); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "chain"); request.put ("block", block->hash ().to_string ()); request.put ("count", std::to_string (std::numeric_limits::max ())); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -905,13 +1078,18 @@ TEST (rpc, chain_limit) ASSERT_FALSE (genesis.is_zero ()); auto block (system.wallet (0)->send_action (nano::test_genesis_key.pub, key.pub, 1)); ASSERT_NE (nullptr, block); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "chain"); request.put ("block", block->hash ().to_string ()); request.put ("count", 1); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -937,14 +1115,19 @@ TEST (rpc, chain_offset) ASSERT_FALSE (genesis.is_zero ()); auto block (system.wallet (0)->send_action (nano::test_genesis_key.pub, key.pub, 1)); ASSERT_NE (nullptr, block); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "chain"); request.put ("block", block->hash ().to_string ()); request.put ("count", std::to_string (std::numeric_limits::max ())); request.put ("offset", 1); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -975,13 +1158,18 @@ TEST (rpc, frontier) } } nano::keypair key; - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "frontiers"); request.put ("account", nano::account (0).to_account ()); request.put ("count", std::to_string (std::numeric_limits::max ())); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1016,13 +1204,18 @@ TEST (rpc, frontier_limited) } } nano::keypair key; - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "frontiers"); request.put ("account", nano::account (0).to_account ()); request.put ("count", std::to_string (100)); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1047,13 +1240,18 @@ TEST (rpc, frontier_startpoint) } } nano::keypair key; - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "frontiers"); request.put ("account", source.begin ()->first.to_account ()); request.put ("count", std::to_string (1)); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1086,13 +1284,17 @@ TEST (rpc, history) ASSERT_EQ (nano::process_result::progress, node0->ledger.process (transaction, ureceive).code); ASSERT_EQ (nano::process_result::progress, node0->ledger.process (transaction, uchange).code); } - nano::rpc rpc (system.io_ctx, *node0, nano::rpc_config (true)); + enable_ipc_transport_tcp (node0->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node0); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "history"); request.put ("hash", uchange.hash ().to_string ()); request.put ("count", 100); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1150,14 +1352,18 @@ TEST (rpc, account_history) ASSERT_EQ (nano::process_result::progress, node0->ledger.process (transaction, ureceive).code); ASSERT_EQ (nano::process_result::progress, node0->ledger.process (transaction, uchange).code); } - nano::rpc rpc (system.io_ctx, *node0, nano::rpc_config (true)); + enable_ipc_transport_tcp (node0->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node0); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); { boost::property_tree::ptree request; request.put ("action", "account_history"); request.put ("account", nano::genesis_account.to_account ()); request.put ("count", 100); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); while (response.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1204,7 +1410,7 @@ TEST (rpc, account_history) request.put ("account", nano::genesis_account.to_account ()); request.put ("reverse", true); request.put ("count", 1); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); while (response.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1232,7 +1438,7 @@ TEST (rpc, account_history) filtered_accounts.push_back (std::make_pair ("", other_account)); request.add_child ("account_filter", filtered_accounts); request.put ("count", 100); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); while (response.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1252,13 +1458,18 @@ TEST (rpc, history_count) ASSERT_NE (nullptr, send); auto receive (system.wallet (0)->receive_action (*send, nano::test_genesis_key.pub, system.nodes[0]->config.receive_minimum.number ())); ASSERT_NE (nullptr, receive); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "history"); request.put ("hash", receive->hash ().to_string ()); request.put ("count", 1); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1276,14 +1487,18 @@ TEST (rpc, process_block) auto latest (system.nodes[0]->latest (nano::test_genesis_key.pub)); auto & node1 (*system.nodes[0]); nano::send_block send (latest, key.pub, 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "process"); std::string json; send.serialize_json (json); request.put ("block", json); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1307,14 +1522,18 @@ TEST (rpc, process_block_no_work) auto & node1 (*system.nodes[0]); nano::send_block send (latest, key.pub, 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); send.block_work_set (0); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "process"); std::string json; send.serialize_json (json); request.put ("block", json); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1331,14 +1550,18 @@ TEST (rpc, process_republish) auto latest (system.nodes[0]->latest (nano::test_genesis_key.pub)); auto & node1 (*system.nodes[0]); nano::send_block send (latest, key.pub, 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "process"); std::string json; send.serialize_json (json); request.put ("block", json); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1359,7 +1582,11 @@ TEST (rpc, process_subtype_send) auto latest (system.nodes[0]->latest (nano::test_genesis_key.pub)); auto & node1 (*system.nodes[0]); nano::state_block send (nano::genesis_account, latest, nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "process"); @@ -1367,7 +1594,7 @@ TEST (rpc, process_subtype_send) send.serialize_json (json); request.put ("block", json); request.put ("subtype", "receive"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1377,7 +1604,7 @@ TEST (rpc, process_subtype_send) std::error_code ec (nano::error_rpc::invalid_subtype_balance); ASSERT_EQ (response.json.get ("error"), ec.message ()); request.put ("subtype", "change"); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); while (response2.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1385,7 +1612,7 @@ TEST (rpc, process_subtype_send) ASSERT_EQ (200, response2.status); ASSERT_EQ (response2.json.get ("error"), ec.message ()); request.put ("subtype", "send"); - test_response response3 (request, rpc, system.io_ctx); + test_response response3 (request, rpc.config.port, system.io_ctx); while (response3.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1412,7 +1639,11 @@ TEST (rpc, process_subtype_open) } node1.active.start (std::make_shared (send)); nano::state_block open (key.pub, 0, key.pub, nano::Gxrb_ratio, send.hash (), key.prv, key.pub, node1.work_generate_blocking (key.pub)); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "process"); @@ -1420,7 +1651,7 @@ TEST (rpc, process_subtype_open) open.serialize_json (json); request.put ("block", json); request.put ("subtype", "send"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1430,7 +1661,7 @@ TEST (rpc, process_subtype_open) std::error_code ec (nano::error_rpc::invalid_subtype_balance); ASSERT_EQ (response.json.get ("error"), ec.message ()); request.put ("subtype", "epoch"); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); while (response2.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1438,7 +1669,7 @@ TEST (rpc, process_subtype_open) ASSERT_EQ (200, response2.status); ASSERT_EQ (response2.json.get ("error"), ec.message ()); request.put ("subtype", "open"); - test_response response3 (request, rpc, system.io_ctx); + test_response response3 (request, rpc.config.port, system.io_ctx); while (response3.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1464,7 +1695,11 @@ TEST (rpc, process_subtype_receive) } node1.active.start (std::make_shared (send)); nano::state_block receive (nano::test_genesis_key.pub, send.hash (), nano::test_genesis_key.pub, nano::genesis_amount, send.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (send.hash ())); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "process"); @@ -1472,7 +1707,7 @@ TEST (rpc, process_subtype_receive) receive.serialize_json (json); request.put ("block", json); request.put ("subtype", "send"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1482,7 +1717,7 @@ TEST (rpc, process_subtype_receive) std::error_code ec (nano::error_rpc::invalid_subtype_balance); ASSERT_EQ (response.json.get ("error"), ec.message ()); request.put ("subtype", "open"); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); while (response2.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1491,7 +1726,7 @@ TEST (rpc, process_subtype_receive) ec = nano::error_rpc::invalid_subtype_previous; ASSERT_EQ (response2.json.get ("error"), ec.message ()); request.put ("subtype", "receive"); - test_response response3 (request, rpc, system.io_ctx); + test_response response3 (request, rpc.config.port, system.io_ctx); while (response3.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1512,7 +1747,12 @@ TEST (rpc, keepalive) auto node1 (std::make_shared (init1, system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work)); node1->start (); system.nodes.push_back (node1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "keepalive"); @@ -1522,7 +1762,7 @@ TEST (rpc, keepalive) request.put ("port", port); ASSERT_EQ (nullptr, system.nodes[0]->network.udp_channels.channel (node1->network.endpoint ())); ASSERT_EQ (0, system.nodes[0]->network.size ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1546,12 +1786,16 @@ TEST (rpc, payment_init) nano::keypair wallet_id; auto wallet (node1->wallets.create (wallet_id.pub)); ASSERT_TRUE (node1->wallets.items.find (wallet_id.pub) != node1->wallets.items.end ()); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "payment_init"); request.put ("wallet", wallet_id.pub.to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1569,12 +1813,16 @@ TEST (rpc, payment_begin_end) nano::keypair wallet_id; auto wallet (node1->wallets.create (wallet_id.pub)); ASSERT_TRUE (node1->wallets.items.find (wallet_id.pub) != node1->wallets.items.end ()); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "payment_begin"); request1.put ("wallet", wallet_id.pub.to_string ()); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -1609,7 +1857,7 @@ TEST (rpc, payment_begin_end) request2.put ("action", "payment_end"); request2.put ("wallet", wallet_id.pub.to_string ()); request2.put ("account", account.to_account ()); - test_response response2 (request2, rpc, system.io_ctx); + test_response response2 (request2, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -1631,13 +1879,17 @@ TEST (rpc, payment_end_nonempty) auto transaction (node1->wallets.tx_begin ()); system.wallet (0)->init_free_accounts (transaction); auto wallet_id (node1->wallets.items.begin ()->first); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "payment_end"); request1.put ("wallet", wallet_id.to_string ()); request1.put ("account", nano::test_genesis_key.pub.to_account ()); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -1656,12 +1908,16 @@ TEST (rpc, payment_zero_balance) auto transaction (node1->wallets.tx_begin ()); system.wallet (0)->init_free_accounts (transaction); auto wallet_id (node1->wallets.items.begin ()->first); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "payment_begin"); request1.put ("wallet", wallet_id.to_string ()); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -1682,12 +1938,16 @@ TEST (rpc, payment_begin_reuse) nano::keypair wallet_id; auto wallet (node1->wallets.create (wallet_id.pub)); ASSERT_TRUE (node1->wallets.items.find (wallet_id.pub) != node1->wallets.items.end ()); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "payment_begin"); request1.put ("wallet", wallet_id.pub.to_string ()); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -1703,7 +1963,7 @@ TEST (rpc, payment_begin_reuse) request2.put ("action", "payment_end"); request2.put ("wallet", wallet_id.pub.to_string ()); request2.put ("account", account.to_account ()); - test_response response2 (request2, rpc, system.io_ctx); + test_response response2 (request2, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -1712,7 +1972,7 @@ TEST (rpc, payment_begin_reuse) ASSERT_EQ (200, response2.status); ASSERT_TRUE (wallet->exists (account)); ASSERT_NE (wallet->free_accounts.end (), wallet->free_accounts.find (account)); - test_response response3 (request1, rpc, system.io_ctx); + test_response response3 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response3.status == 0) { @@ -1738,12 +1998,16 @@ TEST (rpc, payment_begin_locked) ASSERT_TRUE (wallet->store.attempt_password (transaction, "")); } ASSERT_TRUE (node1->wallets.items.find (wallet_id.pub) != node1->wallets.items.end ()); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "payment_begin"); request1.put ("wallet", wallet_id.pub.to_string ()); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -1761,14 +2025,18 @@ TEST (rpc, payment_wait) nano::keypair key; system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->insert_adhoc (key.prv); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "payment_wait"); request1.put ("account", key.pub.to_account ()); request1.put ("amount", nano::amount (nano::Mxrb_ratio).to_string_dec ()); request1.put ("timeout", "100"); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -1781,7 +2049,7 @@ TEST (rpc, payment_wait) system.alarm.add (std::chrono::steady_clock::now () + std::chrono::milliseconds (500), [&]() { system.wallet (0)->send_action (nano::test_genesis_key.pub, key.pub, nano::Mxrb_ratio); }); - test_response response2 (request1, rpc, system.io_ctx); + test_response response2 (request1, rpc.config.port, system.io_ctx); while (response2.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1789,7 +2057,7 @@ TEST (rpc, payment_wait) ASSERT_EQ (200, response2.status); ASSERT_EQ ("success", response2.json.get ("status")); request1.put ("amount", nano::amount (nano::Mxrb_ratio * 2).to_string_dec ()); - test_response response3 (request1, rpc, system.io_ctx); + test_response response3 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response3.status == 0) { @@ -1803,12 +2071,17 @@ TEST (rpc, peers) { nano::system system (24000, 2); nano::endpoint endpoint (boost::asio::ip::address_v6::from_string ("fc00::1"), 4000); - system.nodes[0]->network.udp_channels.insert (endpoint, nano::protocol_version); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + node->network.udp_channels.insert (endpoint, nano::protocol_version); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "peers"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1828,13 +2101,18 @@ TEST (rpc, peers_node_id) { nano::system system (24000, 2); nano::endpoint endpoint (boost::asio::ip::address_v6::from_string ("fc00::1"), 4000); - system.nodes[0]->network.udp_channels.insert (endpoint, nano::protocol_version); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + node->network.udp_channels.insert (endpoint, nano::protocol_version); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "peers"); request.put ("peer_details", true); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1864,14 +2142,19 @@ TEST (rpc, pending) { ASSERT_NO_ERROR (system.poll ()); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "pending"); request.put ("account", key1.pub.to_account ()); request.put ("count", "100"); { - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1885,7 +2168,7 @@ TEST (rpc, pending) } request.put ("sorting", "true"); // Sorting test { - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1901,7 +2184,7 @@ TEST (rpc, pending) } request.put ("threshold", "100"); // Threshold test { - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1927,7 +2210,7 @@ TEST (rpc, pending) } request.put ("threshold", "101"); { - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); while (response.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -1940,7 +2223,7 @@ TEST (rpc, pending) request.put ("source", "true"); request.put ("min_version", "true"); { - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -1964,25 +2247,6 @@ TEST (rpc, pending) } } -TEST (rpc_config, serialization) -{ - nano::rpc_config config1; - config1.address = boost::asio::ip::address_v6::any (); - config1.port = 10; - config1.enable_control = true; - nano::jsonconfig tree; - config1.serialize_json (tree); - nano::rpc_config config2; - ASSERT_NE (config2.address, config1.address); - ASSERT_NE (config2.port, config1.port); - ASSERT_NE (config2.enable_control, config1.enable_control); - bool upgraded{ false }; - config2.deserialize_json (upgraded, tree); - ASSERT_EQ (config2.address, config1.address); - ASSERT_EQ (config2.port, config1.port); - ASSERT_EQ (config2.enable_control, config1.enable_control); -} - TEST (rpc, search_pending) { nano::system system (24000, 1); @@ -1994,12 +2258,17 @@ TEST (rpc, search_pending) auto transaction (system.nodes[0]->store.tx_begin (true)); ASSERT_EQ (nano::process_result::progress, system.nodes[0]->ledger.process (transaction, block).code); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "search_pending"); request.put ("wallet", wallet); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -2021,11 +2290,15 @@ TEST (rpc, version) nano::keypair key; system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->insert_adhoc (key.prv); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "version"); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2070,13 +2343,17 @@ TEST (rpc, work_generate) nano::keypair key; system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->insert_adhoc (key.prv); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); nano::block_hash hash1 (1); boost::property_tree::ptree request1; request1.put ("action", "work_generate"); request1.put ("hash", hash1.to_string ()); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2094,7 +2371,12 @@ TEST (rpc, work_generate_difficulty) nano::system system (24000, 1); nano::node_init init1; auto node1 (system.nodes[0]); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1->config.ipc_config.transport_tcp); + nano::node_rpc_config node_rpc_config; + nano::ipc::ipc_server ipc_server (*node1, node_rpc_config); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); nano::block_hash hash1 (1); uint64_t difficulty1 (0xfff0000000000000); @@ -2102,7 +2384,7 @@ TEST (rpc, work_generate_difficulty) request1.put ("action", "work_generate"); request1.put ("hash", hash1.to_string ()); request1.put ("difficulty", nano::to_string_hex (difficulty1)); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (10s); while (response1.status == 0) { @@ -2117,7 +2399,7 @@ TEST (rpc, work_generate_difficulty) ASSERT_GE (result_difficulty1, difficulty1); uint64_t difficulty2 (0xffff000000000000); request1.put ("difficulty", nano::to_string_hex (difficulty2)); - test_response response2 (request1, rpc, system.io_ctx); + test_response response2 (request1, rpc.config.port, system.io_ctx); system.deadline_set (20s); while (response2.status == 0) { @@ -2130,9 +2412,9 @@ TEST (rpc, work_generate_difficulty) uint64_t result_difficulty2; ASSERT_FALSE (nano::work_validate (hash1, work2, &result_difficulty2)); ASSERT_GE (result_difficulty2, difficulty2); - uint64_t difficulty3 (rpc.config.max_work_generate_difficulty + 1); + uint64_t difficulty3 (node_rpc_config.max_work_generate_difficulty + 1); request1.put ("difficulty", nano::to_string_hex (difficulty3)); - test_response response3 (request1, rpc, system.io_ctx); + test_response response3 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response3.status == 0) { @@ -2151,7 +2433,11 @@ TEST (rpc, work_cancel) nano::keypair key; system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->insert_adhoc (key.prv); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); nano::block_hash hash1 (1); boost::property_tree::ptree request1; @@ -2164,7 +2450,7 @@ TEST (rpc, work_cancel) system.work.generate (hash1, [&done](boost::optional work_a) { done = !work_a; }); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); std::error_code ec; while (response1.status == 0) { @@ -2184,7 +2470,11 @@ TEST (rpc, work_peer_bad) nano::keypair key; system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->insert_adhoc (key.prv); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); node2.config.work_peers.push_back (std::make_pair (boost::asio::ip::address_v6::any ().to_string (), 0)); nano::block_hash hash1 (1); @@ -2208,7 +2498,11 @@ TEST (rpc, work_peer_one) nano::keypair key; system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->insert_adhoc (key.prv); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); node2.config.work_peers.push_back (std::make_pair (node1.network.endpoint ().address ().to_string (), rpc.config.port)); nano::keypair key1; @@ -2237,19 +2531,29 @@ TEST (rpc, work_peer_many) nano::keypair key; nano::rpc_config config2 (true); config2.port += 0; - nano::rpc rpc2 (system2.io_ctx, node2, config2); + enable_ipc_transport_tcp (node2.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server2 (node2); + nano::ipc_rpc_processor ipc_rpc_processor2 (system2.io_ctx, config2); + nano::rpc rpc2 (system2.io_ctx, config2, ipc_rpc_processor2); rpc2.start (); nano::rpc_config config3 (true); config3.port += 1; - nano::rpc rpc3 (system3.io_ctx, node3, config3); + enable_ipc_transport_tcp (node3.config.ipc_config.transport_tcp, node3.network_params.network.default_ipc_port + 1); + nano::ipc::ipc_server ipc_server3 (node3); + nano::ipc_rpc_processor ipc_rpc_processor3 (system3.io_ctx, config3); + nano::rpc rpc3 (system3.io_ctx, config3, ipc_rpc_processor3); rpc3.start (); nano::rpc_config config4 (true); config4.port += 2; - nano::rpc rpc4 (system4.io_ctx, node4, config4); + enable_ipc_transport_tcp (node4.config.ipc_config.transport_tcp, node4.network_params.network.default_ipc_port + 2); + nano::ipc::ipc_server ipc_server4 (node4); + nano::ipc_rpc_processor ipc_rpc_processor4 (system4.io_ctx, config4); + nano::rpc rpc4 (system2.io_ctx, config4, ipc_rpc_processor4); rpc4.start (); node1.config.work_peers.push_back (std::make_pair (node2.network.endpoint ().address ().to_string (), rpc2.config.port)); node1.config.work_peers.push_back (std::make_pair (node3.network.endpoint ().address ().to_string (), rpc3.config.port)); node1.config.work_peers.push_back (std::make_pair (node4.network.endpoint ().address ().to_string (), rpc4.config.port)); + for (auto i (0); i < 10; ++i) { nano::keypair key1; @@ -2272,11 +2576,15 @@ TEST (rpc, block_count) nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "block_count"); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2292,11 +2600,15 @@ TEST (rpc, frontier_count) nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "frontier_count"); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2311,11 +2623,15 @@ TEST (rpc, account_count) nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "account_count"); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2330,11 +2646,15 @@ TEST (rpc, available_supply) nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "available_supply"); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2345,7 +2665,7 @@ TEST (rpc, available_supply) system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); nano::keypair key; auto block (system.wallet (0)->send_action (nano::test_genesis_key.pub, key.pub, 1)); - test_response response2 (request1, rpc, system.io_ctx); + test_response response2 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -2354,7 +2674,7 @@ TEST (rpc, available_supply) ASSERT_EQ (200, response2.status); ASSERT_EQ ("1", response2.json.get ("available")); auto block2 (system.wallet (0)->send_action (nano::test_genesis_key.pub, 0, 100)); // Sending to burning 0 account - test_response response3 (request1, rpc, system.io_ctx); + test_response response3 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response3.status == 0) { @@ -2369,12 +2689,16 @@ TEST (rpc, mrai_to_raw) nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "mrai_to_raw"); request1.put ("amount", "1"); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2389,12 +2713,16 @@ TEST (rpc, mrai_from_raw) nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "mrai_from_raw"); request1.put ("amount", nano::Mxrb_ratio.convert_to ()); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2409,12 +2737,16 @@ TEST (rpc, krai_to_raw) nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "krai_to_raw"); request1.put ("amount", "1"); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2429,12 +2761,16 @@ TEST (rpc, krai_from_raw) nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "krai_from_raw"); request1.put ("amount", nano::kxrb_ratio.convert_to ()); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2449,12 +2785,16 @@ TEST (rpc, nano_to_raw) nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "nano_to_raw"); request1.put ("amount", "1"); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2469,12 +2809,16 @@ TEST (rpc, nano_from_raw) nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request1; request1.put ("action", "nano_from_raw"); request1.put ("amount", nano::xrb_ratio.convert_to ()); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2487,13 +2831,18 @@ TEST (rpc, nano_from_raw) TEST (rpc, account_representative) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; request.put ("account", nano::genesis_account.to_account ()); request.put ("action", "account_representative"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -2508,7 +2857,12 @@ TEST (rpc, account_representative_set) { nano::system system (24000, 1); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; nano::keypair rep; @@ -2516,7 +2870,7 @@ TEST (rpc, account_representative_set) request.put ("representative", rep.pub.to_account ()); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); request.put ("action", "account_representative_set"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -2542,13 +2896,18 @@ TEST (rpc, bootstrap) auto transaction (system1.nodes[0]->store.tx_begin (true)); ASSERT_EQ (nano::process_result::progress, system1.nodes[0]->ledger.process (transaction, send).code); } - nano::rpc rpc (system0.io_ctx, *system0.nodes[0], nano::rpc_config (true)); + auto & node = system0.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system0.io_ctx, rpc_config); + nano::rpc rpc (system0.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "bootstrap"); request.put ("address", "::ffff:127.0.0.1"); request.put ("port", system1.nodes[0]->network.endpoint ().port ()); - test_response response (request, rpc, system0.io_ctx); + test_response response (request, rpc.config.port, system0.io_ctx); while (response.status == 0) { system0.poll (); @@ -2566,13 +2925,18 @@ TEST (rpc, account_remove) nano::system system0 (24000, 1); auto key1 (system0.wallet (0)->deterministic_insert ()); ASSERT_TRUE (system0.wallet (0)->exists (key1)); - nano::rpc rpc (system0.io_ctx, *system0.nodes[0], nano::rpc_config (true)); + auto & node = system0.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system0.io_ctx, rpc_config); + nano::rpc rpc (system0.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "account_remove"); request.put ("wallet", system0.nodes[0]->wallets.items.begin ()->first.to_string ()); request.put ("account", key1.to_account ()); - test_response response (request, rpc, system0.io_ctx); + test_response response (request, rpc.config.port, system0.io_ctx); while (response.status == 0) { system0.poll (); @@ -2583,11 +2947,16 @@ TEST (rpc, account_remove) TEST (rpc, representatives) { nano::system system0 (24000, 1); - nano::rpc rpc (system0.io_ctx, *system0.nodes[0], nano::rpc_config (true)); + auto & node = system0.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system0.io_ctx, rpc_config); + nano::rpc rpc (system0.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "representatives"); - test_response response (request, rpc, system0.io_ctx); + test_response response (request, rpc.config.port, system0.io_ctx); while (response.status == 0) { system0.poll (); @@ -2614,12 +2983,17 @@ TEST (rpc, wallet_seed) auto transaction (system.nodes[0]->wallets.tx_begin ()); system.wallet (0)->store.seed (seed, transaction); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto & node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_seed"); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc_config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -2645,13 +3019,18 @@ TEST (rpc, wallet_change_seed) nano::raw_key prv; nano::deterministic_key (seed.pub, 0, prv.data); auto pub (nano::pub_key (prv.data)); - nano::rpc rpc (system0.io_ctx, *system0.nodes[0], nano::rpc_config (true)); + auto & node = system0.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system0.io_ctx, rpc_config); + nano::rpc rpc (system0.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_change_seed"); request.put ("wallet", system0.nodes[0]->wallets.items.begin ()->first.to_string ()); request.put ("seed", seed.pub.to_string ()); - test_response response (request, rpc, system0.io_ctx); + test_response response (request, rpc.config.port, system0.io_ctx); system0.deadline_set (5s); while (response.status == 0) { @@ -2676,12 +3055,17 @@ TEST (rpc, wallet_frontiers) { nano::system system0 (24000, 1); system0.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - nano::rpc rpc (system0.io_ctx, *system0.nodes[0], nano::rpc_config (true)); + auto & node = system0.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system0.io_ctx, rpc_config); + nano::rpc rpc (system0.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_frontiers"); request.put ("wallet", system0.nodes[0]->wallets.items.begin ()->first.to_string ()); - test_response response (request, rpc, system0.io_ctx); + test_response response (request, rpc.config.port, system0.io_ctx); while (response.status == 0) { system0.poll (); @@ -2699,14 +3083,18 @@ TEST (rpc, wallet_frontiers) TEST (rpc, work_validate) { - nano::network_constants constants; + nano::network_params params; nano::system system (24000, 1); nano::node_init init1; auto & node1 (*system.nodes[0]); nano::keypair key; system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->insert_adhoc (key.prv); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); nano::block_hash hash (1); uint64_t work1 (node1.work_generate_blocking (hash)); @@ -2714,7 +3102,7 @@ TEST (rpc, work_validate) request.put ("action", "work_validate"); request.put ("hash", hash.to_string ()); request.put ("work", nano::to_string_hex (work1)); - test_response response1 (request, rpc, system.io_ctx); + test_response response1 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -2725,7 +3113,7 @@ TEST (rpc, work_validate) ASSERT_EQ ("1", validate_text1); uint64_t work2 (0); request.put ("work", nano::to_string_hex (work2)); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -2736,10 +3124,10 @@ TEST (rpc, work_validate) ASSERT_EQ ("0", validate_text2); uint64_t result_difficulty; ASSERT_FALSE (nano::work_validate (hash, work1, &result_difficulty)); - ASSERT_GE (result_difficulty, constants.publish_threshold); + ASSERT_GE (result_difficulty, params.network.publish_threshold); request.put ("work", nano::to_string_hex (work1)); request.put ("difficulty", nano::to_string_hex (result_difficulty)); - test_response response3 (request, rpc, system.io_ctx); + test_response response3 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response3.status == 0) { @@ -2751,7 +3139,7 @@ TEST (rpc, work_validate) uint64_t difficulty4 (0xfff0000000000000); request.put ("work", nano::to_string_hex (work1)); request.put ("difficulty", nano::to_string_hex (difficulty4)); - test_response response4 (request, rpc, system.io_ctx); + test_response response4 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response4.status == 0) { @@ -2762,7 +3150,7 @@ TEST (rpc, work_validate) ASSERT_EQ (result_difficulty >= difficulty4, validate4); uint64_t work3 (node1.work_generate_blocking (hash, difficulty4)); request.put ("work", nano::to_string_hex (work3)); - test_response response5 (request, rpc, system.io_ctx); + test_response response5 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response5.status == 0) { @@ -2782,13 +3170,18 @@ TEST (rpc, successors) ASSERT_FALSE (genesis.is_zero ()); auto block (system.wallet (0)->send_action (nano::test_genesis_key.pub, key.pub, 1)); ASSERT_NE (nullptr, block); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "successors"); request.put ("block", genesis.to_string ()); request.put ("count", std::to_string (std::numeric_limits::max ())); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -2807,7 +3200,7 @@ TEST (rpc, successors) // RPC chain "reverse" option request.put ("action", "chain"); request.put ("reverse", "true"); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); while (response2.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -2826,11 +3219,16 @@ TEST (rpc, bootstrap_any) auto transaction (system1.nodes[0]->store.tx_begin (true)); ASSERT_EQ (nano::process_result::progress, system1.nodes[0]->ledger.process (transaction, send).code); } - nano::rpc rpc (system0.io_ctx, *system0.nodes[0], nano::rpc_config (true)); + auto & node = system0.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system0.io_ctx, rpc_config); + nano::rpc rpc (system0.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "bootstrap_any"); - test_response response (request, rpc, system0.io_ctx); + test_response response (request, rpc.config.port, system0.io_ctx); while (response.status == 0) { system0.poll (); @@ -2844,18 +3242,22 @@ TEST (rpc, republish) nano::system system (24000, 2); nano::keypair key; nano::genesis genesis; - auto latest (system.nodes[0]->latest (nano::test_genesis_key.pub)); auto & node1 (*system.nodes[0]); + auto latest (node1.latest (nano::test_genesis_key.pub)); nano::send_block send (latest, key.pub, 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); - system.nodes[0]->process (send); + node1.process (send); nano::open_block open (send.hash (), key.pub, key.pub, key.prv, key.pub, node1.work_generate_blocking (key.pub)); - ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (open).code); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + ASSERT_EQ (nano::process_result::progress, node1.process (open).code); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "republish"); request.put ("hash", send.hash ().to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -2878,7 +3280,7 @@ TEST (rpc, republish) request.put ("hash", genesis.hash ().to_string ()); request.put ("count", 1); - test_response response1 (request, rpc, system.io_ctx); + test_response response1 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); system.deadline_set (5s); while (response1.status == 0) @@ -2897,7 +3299,7 @@ TEST (rpc, republish) request.put ("hash", open.hash ().to_string ()); request.put ("sources", 2); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); while (response2.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -2926,13 +3328,18 @@ TEST (rpc, deterministic_key) nano::account account0 (system0.wallet (0)->deterministic_insert ()); nano::account account1 (system0.wallet (0)->deterministic_insert ()); nano::account account2 (system0.wallet (0)->deterministic_insert ()); - nano::rpc rpc (system0.io_ctx, *system0.nodes[0], nano::rpc_config (true)); + auto & node = system0.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system0.io_ctx, rpc_config); + nano::rpc rpc (system0.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "deterministic_key"); request.put ("seed", seed.data.to_string ()); request.put ("index", "0"); - test_response response0 (request, rpc, system0.io_ctx); + test_response response0 (request, rpc.config.port, system0.io_ctx); while (response0.status == 0) { system0.poll (); @@ -2941,7 +3348,7 @@ TEST (rpc, deterministic_key) std::string validate_text (response0.json.get ("account")); ASSERT_EQ (account0.to_account (), validate_text); request.put ("index", "2"); - test_response response1 (request, rpc, system0.io_ctx); + test_response response1 (request, rpc.config.port, system0.io_ctx); while (response1.status == 0) { system0.poll (); @@ -2955,7 +3362,12 @@ TEST (rpc, deterministic_key) TEST (rpc, accounts_balances) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "accounts_balances"); @@ -2964,7 +3376,7 @@ TEST (rpc, accounts_balances) entry.put ("", nano::test_genesis_key.pub.to_account ()); peers_l.push_back (std::make_pair ("", entry)); request.add_child ("accounts", peers_l); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -2986,7 +3398,12 @@ TEST (rpc, accounts_frontiers) { nano::system system (24000, 1); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "accounts_frontiers"); @@ -2995,7 +3412,7 @@ TEST (rpc, accounts_frontiers) entry.put ("", nano::test_genesis_key.pub.to_account ()); peers_l.push_back (std::make_pair ("", entry)); request.add_child ("accounts", peers_l); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3022,7 +3439,12 @@ TEST (rpc, accounts_pending) { ASSERT_NO_ERROR (system.poll ()); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "accounts_pending"); @@ -3033,7 +3455,7 @@ TEST (rpc, accounts_pending) request.add_child ("accounts", peers_l); request.put ("count", "100"); { - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3050,7 +3472,7 @@ TEST (rpc, accounts_pending) } request.put ("sorting", "true"); // Sorting test { - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3069,7 +3491,7 @@ TEST (rpc, accounts_pending) } request.put ("threshold", "100"); // Threshold test { - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3096,7 +3518,7 @@ TEST (rpc, accounts_pending) } request.put ("source", "true"); { - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3125,7 +3547,12 @@ TEST (rpc, accounts_pending) TEST (rpc, blocks) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "blocks"); @@ -3134,7 +3561,7 @@ TEST (rpc, blocks) entry.put ("", system.nodes[0]->latest (nano::genesis_account).to_string ()); peers_l.push_back (std::make_pair ("", entry)); request.add_child ("hashes", peers_l); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3163,12 +3590,17 @@ TEST (rpc, wallet_info) system.wallet (0)->store.erase (transaction, account); } account = system.wallet (0)->deterministic_insert (); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_info"); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3193,12 +3625,17 @@ TEST (rpc, wallet_balances) { nano::system system0 (24000, 1); system0.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - nano::rpc rpc (system0.io_ctx, *system0.nodes[0], nano::rpc_config (true)); + auto & node = system0.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system0.io_ctx, rpc_config); + nano::rpc rpc (system0.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_balances"); request.put ("wallet", system0.nodes[0]->wallets.items.begin ()->first.to_string ()); - test_response response (request, rpc, system0.io_ctx); + test_response response (request, rpc.config.port, system0.io_ctx); while (response.status == 0) { system0.poll (); @@ -3217,7 +3654,7 @@ TEST (rpc, wallet_balances) system0.wallet (0)->insert_adhoc (key.prv); auto send (system0.wallet (0)->send_action (nano::test_genesis_key.pub, key.pub, 1)); request.put ("threshold", "2"); - test_response response1 (request, rpc, system0.io_ctx); + test_response response1 (request, rpc.config.port, system0.io_ctx); while (response1.status == 0) { system0.poll (); @@ -3246,12 +3683,17 @@ TEST (rpc, pending_exists) { ASSERT_NO_ERROR (system.poll ()); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "pending_exists"); request.put ("hash", hash0.to_string ()); - test_response response0 (request, rpc, system.io_ctx); + test_response response0 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response0.status == 0) { @@ -3261,7 +3703,7 @@ TEST (rpc, pending_exists) std::string exists_text (response0.json.get ("exists")); ASSERT_EQ ("0", exists_text); request.put ("hash", block1->hash ().to_string ()); - test_response response1 (request, rpc, system.io_ctx); + test_response response1 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -3286,13 +3728,18 @@ TEST (rpc, wallet_pending) ++iterations; ASSERT_LT (iterations, 200); } - nano::rpc rpc (system0.io_ctx, *system0.nodes[0], nano::rpc_config (true)); + auto & node = system0.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system0.io_ctx, rpc_config); + nano::rpc rpc (system0.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_pending"); request.put ("wallet", system0.nodes[0]->wallets.items.begin ()->first.to_string ()); request.put ("count", "100"); - test_response response (request, rpc, system0.io_ctx); + test_response response (request, rpc.config.port, system0.io_ctx); while (response.status == 0) { system0.poll (); @@ -3307,7 +3754,7 @@ TEST (rpc, wallet_pending) ASSERT_EQ (block1->hash (), hash1); } request.put ("threshold", "100"); // Threshold test - test_response response0 (request, rpc, system0.io_ctx); + test_response response0 (request, rpc.config.port, system0.io_ctx); while (response0.status == 0) { system0.poll (); @@ -3334,7 +3781,7 @@ TEST (rpc, wallet_pending) } ASSERT_EQ (blocks[block1->hash ()], 100); request.put ("threshold", "101"); - test_response response1 (request, rpc, system0.io_ctx); + test_response response1 (request, rpc.config.port, system0.io_ctx); while (response1.status == 0) { system0.poll (); @@ -3345,7 +3792,7 @@ TEST (rpc, wallet_pending) request.put ("threshold", "0"); request.put ("source", "true"); request.put ("min_version", "true"); - test_response response2 (request, rpc, system0.io_ctx); + test_response response2 (request, rpc.config.port, system0.io_ctx); while (response2.status == 0) { system0.poll (); @@ -3374,11 +3821,16 @@ TEST (rpc, wallet_pending) TEST (rpc, receive_minimum) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "receive_minimum"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3392,13 +3844,18 @@ TEST (rpc, receive_minimum) TEST (rpc, receive_minimum_set) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "receive_minimum_set"); request.put ("amount", "100"); ASSERT_NE (system.nodes[0]->config.receive_minimum.to_string_dec (), "100"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3415,13 +3872,18 @@ TEST (rpc, work_get) nano::system system (24000, 1); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->work_cache_blocking (nano::test_genesis_key.pub, system.nodes[0]->latest (nano::test_genesis_key.pub)); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "work_get"); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); request.put ("account", nano::test_genesis_key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3440,12 +3902,17 @@ TEST (rpc, wallet_work_get) nano::system system (24000, 1); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->work_cache_blocking (nano::test_genesis_key.pub, system.nodes[0]->latest (nano::test_genesis_key.pub)); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_work_get"); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3469,14 +3936,19 @@ TEST (rpc, work_set) nano::system system (24000, 1); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); uint64_t work0 (100); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "work_set"); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); request.put ("account", nano::test_genesis_key.pub.to_account ()); request.put ("work", nano::to_string_hex (work0)); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3501,11 +3973,16 @@ TEST (rpc, search_pending_all) auto transaction (system.nodes[0]->store.tx_begin (true)); ASSERT_EQ (nano::process_result::progress, system.nodes[0]->ledger.process (transaction, block).code); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "search_pending_all"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3538,13 +4015,17 @@ TEST (rpc, wallet_republish) system.nodes[0]->process (send); nano::open_block open (send.hash (), key.pub, key.pub, key.prv, key.pub, node1.work_generate_blocking (key.pub)); ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (open).code); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_republish"); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); request.put ("count", 1); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3574,12 +4055,16 @@ TEST (rpc, delegators) system.nodes[0]->process (send); nano::open_block open (send.hash (), nano::test_genesis_key.pub, key.pub, key.prv, key.pub, node1.work_generate_blocking (key.pub)); ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (open).code); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "delegators"); request.put ("account", nano::test_genesis_key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3604,17 +4089,21 @@ TEST (rpc, delegators_count) system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->insert_adhoc (key.prv); auto & node1 (*system.nodes[0]); - auto latest (system.nodes[0]->latest (nano::test_genesis_key.pub)); + auto latest (node1.latest (nano::test_genesis_key.pub)); nano::send_block send (latest, key.pub, 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); - system.nodes[0]->process (send); + node1.process (send); nano::open_block open (send.hash (), nano::test_genesis_key.pub, key.pub, key.prv, key.pub, node1.work_generate_blocking (key.pub)); ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (open).code); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "delegators_count"); request.put ("account", nano::test_genesis_key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3641,17 +4130,21 @@ TEST (rpc, account_info) { auto transaction = system.nodes[0]->store.tx_begin_write (); nano::account_info account_info; - ASSERT_FALSE (system.nodes[0]->store.account_get (transaction, nano::test_genesis_key.pub, account_info)); + ASSERT_FALSE (node1.store.account_get (transaction, nano::test_genesis_key.pub, account_info)); account_info.confirmation_height = 1; - system.nodes[0]->store.account_put (transaction, nano::test_genesis_key.pub, account_info); + node1.store.account_put (transaction, nano::test_genesis_key.pub, account_info); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "account_info"); request.put ("account", nano::test_genesis_key.pub.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3683,7 +4176,7 @@ TEST (rpc, account_info) request.put ("weight", "true"); request.put ("pending", "1"); request.put ("representative", "1"); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -3704,7 +4197,11 @@ TEST (rpc, json_block_input) nano::keypair key; auto & node1 (*system.nodes[0]); nano::state_block send (nano::genesis_account, node1.latest (nano::test_genesis_key.pub), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "sign"); @@ -3717,7 +4214,7 @@ TEST (rpc, json_block_input) boost::property_tree::ptree json; send.serialize_json (json); request.add_child ("block", json); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); while (response.status == 0) { system.poll (); @@ -3744,13 +4241,17 @@ TEST (rpc, json_block_output) auto latest (system.nodes[0]->latest (nano::test_genesis_key.pub)); nano::send_block send (latest, key.pub, 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); system.nodes[0]->process (send); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "block_info"); request.put ("json_block", "true"); request.put ("hash", send.hash ().to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3767,7 +4268,12 @@ TEST (rpc, json_block_output) TEST (rpc, blocks_info) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "blocks_info"); @@ -3776,7 +4282,7 @@ TEST (rpc, blocks_info) entry.put ("", system.nodes[0]->latest (nano::genesis_account).to_string ()); peers_l.push_back (std::make_pair ("", entry)); request.add_child ("hashes", peers_l); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3804,7 +4310,7 @@ TEST (rpc, blocks_info) // Test for optional values request.put ("source", "true"); request.put ("pending", "1"); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -3833,7 +4339,11 @@ TEST (rpc, blocks_info_subtype) ASSERT_NE (nullptr, receive); auto change (system.wallet (0)->change_action (nano::test_genesis_key.pub, key.pub)); ASSERT_NE (nullptr, change); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "blocks_info"); @@ -3846,7 +4356,7 @@ TEST (rpc, blocks_info_subtype) entry.put ("", change->hash ().to_string ()); peers_l.push_back (std::make_pair ("", entry)); request.add_child ("hashes", peers_l); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3868,13 +4378,17 @@ TEST (rpc, work_peers_all) nano::node_init init1; auto & node1 (*system.nodes[0]); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "work_peer_add"); request.put ("address", "::1"); request.put ("port", "0"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3885,7 +4399,7 @@ TEST (rpc, work_peers_all) ASSERT_TRUE (success.empty ()); boost::property_tree::ptree request1; request1.put ("action", "work_peers"); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -3902,7 +4416,7 @@ TEST (rpc, work_peers_all) ASSERT_EQ ("::1:0", peers[0]); boost::property_tree::ptree request2; request2.put ("action", "work_peers_clear"); - test_response response2 (request2, rpc, system.io_ctx); + test_response response2 (request2, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -3911,7 +4425,7 @@ TEST (rpc, work_peers_all) ASSERT_EQ (200, response2.status); success = response2.json.get ("success", ""); ASSERT_TRUE (success.empty ()); - test_response response3 (request1, rpc, system.io_ctx); + test_response response3 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response3.status == 0) { @@ -3930,11 +4444,16 @@ TEST (rpc, block_count_type) ASSERT_NE (nullptr, send); auto receive (system.wallet (0)->receive_action (*send, nano::test_genesis_key.pub, system.nodes[0]->config.receive_minimum.number ())); ASSERT_NE (nullptr, receive); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "block_count_type"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -3961,19 +4480,23 @@ TEST (rpc, ledger) system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->insert_adhoc (key.prv); auto & node1 (*system.nodes[0]); - auto latest (system.nodes[0]->latest (nano::test_genesis_key.pub)); + auto latest (node1.latest (nano::test_genesis_key.pub)); nano::send_block send (latest, key.pub, 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); - system.nodes[0]->process (send); + node1.process (send); nano::open_block open (send.hash (), nano::test_genesis_key.pub, key.pub, key.prv, key.pub, node1.work_generate_blocking (key.pub)); - ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (open).code); + ASSERT_EQ (nano::process_result::progress, node1.process (open).code); auto time (nano::seconds_since_epoch ()); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "ledger"); request.put ("sorting", "1"); request.put ("count", "1"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4006,7 +4529,7 @@ TEST (rpc, ledger) request.put ("weight", "1"); request.put ("pending", "1"); request.put ("representative", "true"); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -4029,13 +4552,18 @@ TEST (rpc, ledger) TEST (rpc, accounts_create) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "accounts_create"); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); request.put ("count", "8"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4061,12 +4589,16 @@ TEST (rpc, block_create) system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); system.wallet (0)->insert_adhoc (key.prv); auto & node1 (*system.nodes[0]); - auto latest (system.nodes[0]->latest (nano::test_genesis_key.pub)); + auto latest (node1.latest (nano::test_genesis_key.pub)); auto send_work = node1.work_generate_blocking (latest); nano::send_block send (latest, key.pub, 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, send_work); auto open_work = node1.work_generate_blocking (key.pub); nano::open_block open (send.hash (), nano::test_genesis_key.pub, key.pub, key.prv, key.pub, open_work); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "block_create"); @@ -4077,7 +4609,7 @@ TEST (rpc, block_create) request.put ("amount", "340282366920938463463374607431768211355"); request.put ("destination", key.pub.to_account ()); request.put ("work", nano::to_string_hex (send_work)); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4102,7 +4634,7 @@ TEST (rpc, block_create) request1.put ("representative", nano::test_genesis_key.pub.to_account ()); request1.put ("source", send.hash ().to_string ()); request1.put ("work", nano::to_string_hex (open_work)); - test_response response1 (request1, rpc, system.io_ctx); + test_response response1 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response1.status == 0) { @@ -4118,7 +4650,7 @@ TEST (rpc, block_create) ASSERT_EQ (open.hash (), open_block->hash ()); ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (open).code); request1.put ("representative", key.pub.to_account ()); - test_response response2 (request1, rpc, system.io_ctx); + test_response response2 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -4131,7 +4663,7 @@ TEST (rpc, block_create) nano::change_block change (open.hash (), key.pub, key.prv, key.pub, change_work); request1.put ("type", "change"); request1.put ("work", nano::to_string_hex (change_work)); - test_response response4 (request1, rpc, system.io_ctx); + test_response response4 (request1, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response4.status == 0) { @@ -4156,7 +4688,7 @@ TEST (rpc, block_create) request2.put ("source", send2.hash ().to_string ()); request2.put ("previous", change.hash ().to_string ()); request2.put ("work", nano::to_string_hex (node1.work_generate_blocking (change.hash ()))); - test_response response5 (request2, rpc, system.io_ctx); + test_response response5 (request2, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response5.status == 0) { @@ -4190,9 +4722,14 @@ TEST (rpc, block_create_state) request.put ("balance", (nano::genesis_amount - nano::Gxrb_ratio).convert_to ()); request.put ("link", key.pub.to_account ()); request.put ("work", nano::to_string_hex (system.nodes[0]->work_generate_blocking (genesis.hash ()))); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4230,9 +4767,14 @@ TEST (rpc, block_create_state_open) request.put ("balance", nano::Gxrb_ratio.convert_to ()); request.put ("link", send_block->hash ().to_string ()); request.put ("work", nano::to_string_hex (system.nodes[0]->work_generate_blocking (send_block->hash ()))); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4277,9 +4819,14 @@ TEST (rpc, block_create_state_request_work) request.put ("balance", (nano::genesis_amount - nano::Gxrb_ratio).convert_to ()); request.put ("link", key.pub.to_account ()); request.put ("previous", previous); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4302,14 +4849,18 @@ TEST (rpc, block_hash) auto latest (system.nodes[0]->latest (nano::test_genesis_key.pub)); auto & node1 (*system.nodes[0]); nano::send_block send (latest, key.pub, 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "block_hash"); std::string json; send.serialize_json (json); request.put ("block", json); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4323,7 +4874,12 @@ TEST (rpc, block_hash) TEST (rpc, wallet_lock) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; @@ -4334,7 +4890,7 @@ TEST (rpc, wallet_lock) } request.put ("wallet", wallet); request.put ("action", "wallet_lock"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4350,14 +4906,19 @@ TEST (rpc, wallet_lock) TEST (rpc, wallet_locked) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; system.nodes[0]->wallets.items.begin ()->first.encode_hex (wallet); request.put ("wallet", wallet); request.put ("action", "wallet_locked"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4371,8 +4932,12 @@ TEST (rpc, wallet_locked) TEST (rpc, wallet_create_fail) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); - auto node = system.nodes[0]; + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); // lmdb_max_dbs should be removed once the wallet store is refactored to support more wallets. for (int i = 0; i < 127; i++) { @@ -4382,7 +4947,7 @@ TEST (rpc, wallet_create_fail) rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_create"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4402,16 +4967,20 @@ TEST (rpc, wallet_ledger) nano::send_block send (latest, key.pub, 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, node1.work_generate_blocking (latest)); system.nodes[0]->process (send); nano::open_block open (send.hash (), nano::test_genesis_key.pub, key.pub, key.prv, key.pub, node1.work_generate_blocking (key.pub)); - ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (open).code); + ASSERT_EQ (nano::process_result::progress, node1.process (open).code); auto time (nano::seconds_since_epoch ()); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_ledger"); request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); request.put ("sorting", "1"); request.put ("count", "1"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4444,7 +5013,7 @@ TEST (rpc, wallet_ledger) request.put ("weight", "true"); request.put ("pending", "1"); request.put ("representative", "false"); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response2.status == 0) { @@ -4466,7 +5035,12 @@ TEST (rpc, wallet_ledger) TEST (rpc, wallet_add_watch) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; std::string wallet; @@ -4478,7 +5052,7 @@ TEST (rpc, wallet_add_watch) entry.put ("", nano::test_genesis_key.pub.to_account ()); peers_l.push_back (std::make_pair ("", entry)); request.add_child ("accounts", peers_l); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4503,11 +5077,15 @@ TEST (rpc, online_reps) { ASSERT_NO_ERROR (system.poll ()); } - nano::rpc rpc (system.io_ctx, *system.nodes[1], nano::rpc_config (true)); + enable_ipc_transport_tcp (system.nodes[1]->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*system.nodes[1]); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "representatives_online"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4520,13 +5098,15 @@ TEST (rpc, online_reps) ASSERT_EQ (nano::test_genesis_key.pub.to_account (), item->second.get ("")); boost::optional weight (item->second.get_optional ("weight")); ASSERT_FALSE (weight.is_initialized ()); + system.deadline_set (5s); while (system.nodes[1]->block (send_block->hash ()) == nullptr) { ASSERT_NO_ERROR (system.poll ()); } //Test weight option request.put ("weight", "true"); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); while (response2.status == 0) { ASSERT_NO_ERROR (system.poll ()); @@ -4541,18 +5121,21 @@ TEST (rpc, online_reps) auto new_rep (system.wallet (1)->deterministic_insert ()); auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, new_rep, system.nodes[0]->config.receive_minimum.number ())); ASSERT_NE (nullptr, send); + system.deadline_set (5s); while (system.nodes[1]->block (send->hash ()) == nullptr) { ASSERT_NO_ERROR (system.poll ()); } auto receive (system.wallet (1)->receive_action (*send, new_rep, system.nodes[0]->config.receive_minimum.number ())); ASSERT_NE (nullptr, receive); + system.deadline_set (5s); while (system.nodes[1]->block (receive->hash ()) == nullptr) { ASSERT_NO_ERROR (system.poll ()); } auto change (system.wallet (0)->change_action (nano::test_genesis_key.pub, new_rep)); ASSERT_NE (nullptr, change); + system.deadline_set (5s); while (system.nodes[1]->block (change->hash ()) == nullptr) { ASSERT_NO_ERROR (system.poll ()); @@ -4567,7 +5150,7 @@ TEST (rpc, online_reps) boost::property_tree::ptree filtered_accounts; filtered_accounts.push_back (std::make_pair ("", child_rep)); request.add_child ("accounts", filtered_accounts); - test_response response3 (request, rpc, system.io_ctx); + test_response response3 (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response3.status == 0) { @@ -4593,11 +5176,16 @@ TEST (rpc, confirmation_history) { ASSERT_NO_ERROR (system.poll ()); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "confirmation_history"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4632,12 +5220,17 @@ TEST (rpc, confirmation_history_hash) { ASSERT_NO_ERROR (system.poll ()); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "confirmation_history"); request.put ("hash", send2->hash ().to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4669,12 +5262,17 @@ TEST (rpc, block_confirm) auto transaction (system.nodes[0]->store.tx_begin (true)); ASSERT_EQ (nano::process_result::progress, system.nodes[0]->ledger.process (transaction, *send1).code); } - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "block_confirm"); request.put ("hash", send1->hash ().to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4688,12 +5286,17 @@ TEST (rpc, block_confirm_absent) { nano::system system (24000, 1); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "block_confirm"); request.put ("hash", "0"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4723,12 +5326,17 @@ TEST (rpc, block_confirm_confirmed) ASSERT_TRUE (node->ledger.block_confirmed (transaction, genesis.hash ())); } ASSERT_EQ (0, node->stats.count (nano::stat::type::error, nano::stat::detail::http_callback, nano::stat::dir::out)); - nano::rpc rpc (system.io_ctx, *node, nano::rpc_config (true)); + + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "block_confirm"); request.put ("hash", genesis.hash ().to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4754,11 +5362,16 @@ TEST (rpc, block_confirm_confirmed) TEST (rpc, node_id) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "node_id"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4773,13 +5386,18 @@ TEST (rpc, stats_clear) { nano::system system (24000, 1); nano::keypair key; - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); system.nodes[0]->stats.inc (nano::stat::type::ledger, nano::stat::dir::in); ASSERT_EQ (1, system.nodes[0]->stats.count (nano::stat::type::ledger, nano::stat::dir::in)); boost::property_tree::ptree request; request.put ("action", "stats_clear"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4802,12 +5420,17 @@ TEST (rpc, unopened) ASSERT_NE (nullptr, send); auto send2 (system.wallet (0)->send_action (nano::test_genesis_key.pub, account2, 2)); ASSERT_NE (nullptr, send2); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); { boost::property_tree::ptree request; request.put ("action", "unopened"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4824,7 +5447,7 @@ TEST (rpc, unopened) boost::property_tree::ptree request; request.put ("action", "unopened"); request.put ("account", account2.to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4840,7 +5463,7 @@ TEST (rpc, unopened) boost::property_tree::ptree request; request.put ("action", "unopened"); request.put ("account", nano::account (account2.number () + 1).to_account ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4855,7 +5478,7 @@ TEST (rpc, unopened) boost::property_tree::ptree request; request.put ("action", "unopened"); request.put ("count", "1"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4876,11 +5499,16 @@ TEST (rpc, unopened_burn) ASSERT_FALSE (genesis.is_zero ()); auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::burn_account, 1)); ASSERT_NE (nullptr, send); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "unopened"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4894,11 +5522,16 @@ TEST (rpc, unopened_burn) TEST (rpc, unopened_no_accounts) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "unopened"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4912,12 +5545,17 @@ TEST (rpc, unopened_no_accounts) TEST (rpc, uptime) { nano::system system (24000, 1); - nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "uptime"); std::this_thread::sleep_for (std::chrono::seconds (1)); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4946,12 +5584,17 @@ TEST (rpc, wallet_history) auto send2 (system.wallet (0)->send_action (nano::test_genesis_key.pub, key.pub, node0->config.receive_minimum.number ())); ASSERT_NE (nullptr, send2); system.deadline_set (10s); - nano::rpc rpc (system.io_ctx, *node0, nano::rpc_config (true)); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "wallet_history"); request.put ("wallet", node0->wallets.items.begin ()->first.to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -4997,13 +5640,18 @@ TEST (rpc, sign_hash) nano::keypair key; auto & node1 (*system.nodes[0]); nano::state_block send (nano::genesis_account, node1.latest (nano::test_genesis_key.pub), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::node_rpc_config node_rpc_config; + nano::ipc::ipc_server ipc_server (node1, node_rpc_config); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "sign"); request.put ("hash", send.hash ().to_string ()); request.put ("key", key.prv.data.to_string ()); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); while (response.status == 0) { system.poll (); @@ -5011,8 +5659,8 @@ TEST (rpc, sign_hash) ASSERT_EQ (200, response.status); std::error_code ec (nano::error_rpc::sign_hash_disabled); ASSERT_EQ (response.json.get ("error"), ec.message ()); - rpc.config.enable_sign_hash = true; - test_response response2 (request, rpc, system.io_ctx); + node_rpc_config.enable_sign_hash = true; + test_response response2 (request, rpc.config.port, system.io_ctx); while (response2.status == 0) { system.poll (); @@ -5030,7 +5678,11 @@ TEST (rpc, sign_block) nano::keypair key; auto & node1 (*system.nodes[0]); nano::state_block send (nano::genesis_account, node1.latest (nano::test_genesis_key.pub), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0); - nano::rpc rpc (system.io_ctx, node1, nano::rpc_config (true)); + enable_ipc_transport_tcp (node1.config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (node1); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "sign"); @@ -5042,7 +5694,7 @@ TEST (rpc, sign_block) std::string json; send.serialize_json (json); request.put ("block", json); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); while (response.status == 0) { system.poll (); @@ -5062,7 +5714,11 @@ TEST (rpc, memory_stats) { nano::system system (24000, 1); auto node = system.nodes.front (); - nano::rpc rpc (system.io_ctx, *node, nano::rpc_config (true)); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); // Preliminary test adding to the vote uniquer and checking json output is correct nano::keypair key; @@ -5075,7 +5731,7 @@ TEST (rpc, memory_stats) boost::property_tree::ptree request; request.put ("action", "stats"); request.put ("type", "objects"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); while (response.status == 0) { @@ -5090,21 +5746,27 @@ TEST (rpc, block_confirmed) { nano::system system (24000, 1); auto node = system.nodes.front (); - nano::rpc rpc (system.io_ctx, *node, nano::rpc_config (true)); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; request.put ("action", "block_info"); request.put ("hash", "bad_hash1337"); - test_response response (request, rpc, system.io_ctx); + test_response response (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); while (response.status == 0) { - system.poll (); + ASSERT_NO_ERROR (system.poll ()); } ASSERT_EQ (200, response.status); ASSERT_EQ ("Invalid block hash", response.json.get ("error")); request.put ("hash", "0"); - test_response response1 (request, rpc, system.io_ctx); + test_response response1 (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); while (response1.status == 0) { system.poll (); @@ -5130,10 +5792,11 @@ TEST (rpc, block_confirmed) // This should not be confirmed nano::block_hash latest (node->latest (nano::test_genesis_key.pub)); request.put ("hash", latest.to_string ()); - test_response response2 (request, rpc, system.io_ctx); + test_response response2 (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); while (response2.status == 0) { - system.poll (); + ASSERT_NO_ERROR (system.poll ()); } ASSERT_EQ (200, response2.status); @@ -5159,12 +5822,145 @@ TEST (rpc, block_confirmed) // Requesting confirmation for this should now succeed request.put ("hash", send->hash ().to_string ()); - test_response response3 (request, rpc, system.io_ctx); + test_response response3 (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); while (response3.status == 0) { - system.poll (); + ASSERT_FALSE (system.poll ()); } ASSERT_EQ (200, response3.status); ASSERT_TRUE (response3.json.get ("confirmed")); } + +// This is mainly to check for threading issues with TSAN +TEST (rpc, simultaneous_calls) +{ + // This tests simulatenous calls to the same node in different threads + nano::system system (24000, 1); + auto node = system.nodes.front (); + nano::thread_runner runner (system.io_ctx, node->config.io_threads); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::ipc::ipc_server ipc_server (*node); + nano::rpc_config rpc_config (true); + rpc_config.num_ipc_connections = 8; + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "account_block_count"); + request.put ("account", nano::test_genesis_key.pub.to_account ()); + + constexpr auto num = 100; + std::array, num> test_responses; + for (int i = 0; i < num; ++i) + { + test_responses[i] = std::make_unique (request, system.io_ctx); + } + + std::promise promise; + std::atomic count{ num }; + for (int i = 0; i < num; ++i) + { + // clang-format off + std::thread ([&test_responses, &promise, &count, i, port = rpc.config.port ]() { + test_responses[i]->run (port); + if (--count == 0) + { + promise.set_value (); + } + }) + .detach (); + // clang-format on + } + + promise.get_future ().wait (); + + system.deadline_set (10s); + while (std::any_of (test_responses.begin (), test_responses.end (), [](const auto & test_response) { return test_response->status == 0; })) + { + ASSERT_NO_ERROR (system.poll ()); + } + + for (int i = 0; i < num; ++i) + { + ASSERT_EQ (200, test_responses[i]->status); + std::string block_count_text (test_responses[i]->json.get ("block_count")); + ASSERT_EQ ("1", block_count_text); + } + rpc.stop (); + system.stop (); + ipc_server.stop (); + system.io_ctx.stop (); + runner.join (); +} + +// This tests that the inprocess RPC (i.e without using IPC) works correctly +TEST (rpc, in_process) +{ + nano::system system (24000, 1); + auto node = system.nodes.front (); + enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); + nano::rpc_config rpc_config (true); + nano::node_rpc_config node_rpc_config; + nano::inprocess_rpc_handler inprocess_rpc_handler (*node, node_rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, inprocess_rpc_handler); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "account_balance"); + request.put ("account", nano::test_genesis_key.pub.to_account ()); + test_response response (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); + while (response.status == 0) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_EQ (200, response.status); + std::string balance_text (response.json.get ("balance")); + ASSERT_EQ ("340282366920938463463374607431768211455", balance_text); + std::string pending_text (response.json.get ("pending")); + ASSERT_EQ ("0", pending_text); +} + +TEST (rpc_config, serialization) +{ + nano::rpc_config config1; + config1.address = boost::asio::ip::address_v6::any (); + config1.port = 10; + config1.enable_control = true; + nano::jsonconfig tree; + config1.serialize_json (tree); + nano::rpc_config config2; + ASSERT_NE (config2.address, config1.address); + ASSERT_NE (config2.port, config1.port); + ASSERT_NE (config2.enable_control, config1.enable_control); + bool upgraded{ false }; + config2.deserialize_json (upgraded, tree); + ASSERT_EQ (config2.address, config1.address); + ASSERT_EQ (config2.port, config1.port); + ASSERT_EQ (config2.enable_control, config1.enable_control); +} + +TEST (rpc_config, migrate) +{ + nano::jsonconfig rpc; + rpc.put ("address", "::1"); + rpc.put ("port", 11111); + + bool updated = false; + auto data_path = nano::unique_path (); + boost::filesystem::create_directory (data_path); + nano::node_rpc_config nano_rpc_config; + nano_rpc_config.deserialize_json (updated, rpc, data_path); + ASSERT_TRUE (updated); + + // Check that the rpc config file is created + auto rpc_path = nano::get_rpc_config_path (data_path); + nano::rpc_config rpc_config; + nano::jsonconfig json; + updated = false; + ASSERT_FALSE (json.read_and_update (rpc_config, rpc_path)); + ASSERT_FALSE (updated); + + ASSERT_EQ (rpc_config.port, 11111); +} \ No newline at end of file diff --git a/nano/secure/utility.cpp b/nano/secure/utility.cpp index d4771c72..4e2a57df 100644 --- a/nano/secure/utility.cpp +++ b/nano/secure/utility.cpp @@ -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 (); - } -} diff --git a/nano/secure/utility.hpp b/nano/secure/utility.hpp index 1daccfff..53d1f5b1 100644 --- a/nano/secure/utility.hpp +++ b/nano/secure/utility.hpp @@ -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 (); } diff --git a/nano/slow_test/entry.cpp b/nano/slow_test/entry.cpp index ad34dda4..24a21a74 100644 --- a/nano/slow_test/entry.cpp +++ b/nano/slow_test/entry.cpp @@ -1,7 +1,7 @@ #include -#include 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; }