diff --git a/nano/core_test/block.cpp b/nano/core_test/block.cpp index 4795a79f..59cd0def 100644 --- a/nano/core_test/block.cpp +++ b/nano/core_test/block.cpp @@ -503,6 +503,9 @@ TEST (block_builder, state) .link_hex ("E16DD58C1EFA8B521545B0A74375AA994D9FC43828A4266D75ECF57F07A7EE86") .build (ec); ASSERT_EQ (block->hash ().to_string (), "2D243F8F92CDD0AD94A1D456A6B15F3BE7A6FCBD98D4C5831D06D15C818CD81F"); + ASSERT_TRUE (block->source ().is_zero ()); + ASSERT_TRUE (block->destination ().is_zero ()); + ASSERT_EQ (block->link ().to_string (), "E16DD58C1EFA8B521545B0A74375AA994D9FC43828A4266D75ECF57F07A7EE86"); } TEST (block_builder, state_missing_rep) @@ -573,6 +576,9 @@ TEST (block_builder, open) .source_hex ("E89208DD038FBB269987689621D52292AE9C35941A7484756ECCED92A65093BA") .build (ec); ASSERT_EQ (block->hash ().to_string (), "991CF190094C00F0B68E2E5F75F6BEE95A2E0BD93CEAA4A6734DB9F19B728948"); + ASSERT_EQ (block->source ().to_string (), "E89208DD038FBB269987689621D52292AE9C35941A7484756ECCED92A65093BA"); + ASSERT_TRUE (block->destination ().is_zero ()); + ASSERT_TRUE (block->link ().is_zero ()); } TEST (block_builder, open_equality) @@ -609,6 +615,9 @@ TEST (block_builder, change) .previous_hex ("088EE46429CA936F76C4EAA20B97F6D33E5D872971433EE0C1311BCB98764456") .build (ec); ASSERT_EQ (block->hash ().to_string (), "13552AC3928E93B5C6C215F61879358E248D4A5246B8B3D1EEC5A566EDCEE077"); + ASSERT_TRUE (block->source ().is_zero ()); + ASSERT_TRUE (block->destination ().is_zero ()); + ASSERT_TRUE (block->link ().is_zero ()); } TEST (block_builder, change_equality) @@ -645,6 +654,9 @@ TEST (block_builder, send) .balance_hex ("00F035A9C7D818E7C34148C524FFFFEE") .build (ec); ASSERT_EQ (block->hash ().to_string (), "4560E7B1F3735D082700CFC2852F5D1F378F7418FD24CEF1AD45AB69316F15CD"); + ASSERT_TRUE (block->source ().is_zero ()); + ASSERT_EQ (block->destination ().to_account (), "nano_1gys8r4crpxhp94n4uho5cshaho81na6454qni5gu9n53gksoyy1wcd4udyb"); + ASSERT_TRUE (block->link ().is_zero ()); } TEST (block_builder, send_equality) @@ -704,4 +716,7 @@ TEST (block_builder, receive) .source_hex ("7B2B0A29C1B235FDF9B4DEF2984BB3573BD1A52D28246396FBB3E4C5FE662135") .build (ec); ASSERT_EQ (block->hash ().to_string (), "6C004BF911D9CF2ED75CF6EC45E795122AD5D093FF5A83EDFBA43EC4A3EDC722"); + ASSERT_EQ (block->source ().to_string (), "7B2B0A29C1B235FDF9B4DEF2984BB3573BD1A52D28246396FBB3E4C5FE662135"); + ASSERT_TRUE (block->destination ().is_zero ()); + ASSERT_TRUE (block->link ().is_zero ()); } diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index 219603e4..522847b7 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -727,10 +727,7 @@ TEST (bootstrap_processor, lazy_hash_pruning) } ASSERT_TIMELY (2s, node1->active.empty () && node1->block_confirmed (change2->hash ())); // Pruning action - { - auto transaction (node1->store.tx_begin_write ()); - ASSERT_EQ (3, node1->ledger.pruning_action (transaction, change1->hash (), 1)); - } + node1->ledger_pruning (2, false, false); ASSERT_EQ (9, node0->ledger.cache.block_count); ASSERT_EQ (0, node0->ledger.cache.pruned_count); ASSERT_EQ (5, node1->ledger.cache.block_count); @@ -931,10 +928,7 @@ TEST (bootstrap_processor, lazy_pruning_missing_block) } ASSERT_TIMELY (2s, node1->active.empty () && node1->block_confirmed (state_open->hash ())); // Pruning action - { - auto transaction (node1->store.tx_begin_write ()); - ASSERT_EQ (1, node1->ledger.pruning_action (transaction, send1->hash (), 1)); - } + node1->ledger_pruning (2, false, false); ASSERT_EQ (5, node1->ledger.cache.block_count); ASSERT_EQ (1, node1->ledger.cache.pruned_count); ASSERT_FALSE (node1->ledger.block_exists (send1->hash ())); @@ -1362,10 +1356,7 @@ TEST (bulk, genesis_pruning) election->force_confirm (); } ASSERT_TIMELY (2s, node1->active.empty () && node1->block_confirmed (send3->hash ())); - { - auto transaction (node1->store.tx_begin_write ()); - ASSERT_EQ (2, node1->ledger.pruning_action (transaction, send2->hash (), 2)); - } + node1->ledger_pruning (2, false, false); ASSERT_EQ (2, node1->ledger.cache.pruned_count); ASSERT_EQ (4, node1->ledger.cache.block_count); ASSERT_FALSE (node1->ledger.block_exists (send1->hash ())); diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 50ec952d..048f6325 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -4494,6 +4494,174 @@ TEST (rep_crawler, local) } } +TEST (node, pruning_automatic) +{ + nano::system system; + nano::node_config node_config (nano::get_available_port (), system.logging); + node_config.max_pruning_age = std::chrono::seconds (1); + node_config.enable_voting = false; // Remove after allowing pruned voting + nano::node_flags node_flags; + node_flags.enable_pruning = true; + auto & node1 = *system.add_node (node_config, node_flags); + nano::genesis genesis; + nano::keypair key1; + auto send1 = nano::send_block_builder () + .previous (genesis.hash ()) + .destination (key1.pub) + .balance (nano::genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) + .work (*system.work.generate (genesis.hash ())) + .build_shared (); + auto send2 = nano::send_block_builder () + .previous (send1->hash ()) + .destination (key1.pub) + .balance (0) + .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build_shared (); + // Process as local blocks + node1.process_active (send1); + node1.process_active (send2); + node1.block_processor.flush (); + // Confirm last block to prune previous + { + auto election = node1.active.election (send1->qualified_root ()); + ASSERT_NE (nullptr, election); + election->force_confirm (); + } + ASSERT_TIMELY (2s, node1.block_confirmed (send1->hash ()) && node1.active.active (send2->qualified_root ())); + ASSERT_EQ (0, node1.ledger.cache.pruned_count); + { + auto election = node1.active.election (send2->qualified_root ()); + ASSERT_NE (nullptr, election); + election->force_confirm (); + } + ASSERT_TIMELY (2s, node1.active.empty () && node1.block_confirmed (send2->hash ())); + // Check pruning result + ASSERT_TIMELY (3s, node1.ledger.cache.pruned_count == 1); + ASSERT_TIMELY (2s, node1.store.pruned_count (node1.store.tx_begin_read ()) == 1); // Transaction commit + ASSERT_EQ (1, node1.ledger.cache.pruned_count); + ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_TRUE (node1.ledger.block_exists (genesis.hash ())); + ASSERT_FALSE (node1.ledger.block_exists (send1->hash ())); + ASSERT_TRUE (node1.ledger.block_or_pruned_exists (send1->hash ())); + ASSERT_TRUE (node1.ledger.block_exists (send2->hash ())); +} + +TEST (node, pruning_age) +{ + nano::system system; + nano::node_config node_config (nano::get_available_port (), system.logging); + node_config.enable_voting = false; // Remove after allowing pruned voting + nano::node_flags node_flags; + node_flags.enable_pruning = true; + auto & node1 = *system.add_node (node_config, node_flags); + nano::genesis genesis; + nano::keypair key1; + auto send1 = nano::send_block_builder () + .previous (genesis.hash ()) + .destination (key1.pub) + .balance (nano::genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) + .work (*system.work.generate (genesis.hash ())) + .build_shared (); + auto send2 = nano::send_block_builder () + .previous (send1->hash ()) + .destination (key1.pub) + .balance (0) + .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build_shared (); + // Process as local blocks + node1.process_active (send1); + node1.process_active (send2); + node1.block_processor.flush (); + // Confirm last block to prune previous + { + auto election = node1.active.election (send1->qualified_root ()); + ASSERT_NE (nullptr, election); + election->force_confirm (); + } + ASSERT_TIMELY (2s, node1.block_confirmed (send1->hash ()) && node1.active.active (send2->qualified_root ())); + ASSERT_EQ (0, node1.ledger.cache.pruned_count); + { + auto election = node1.active.election (send2->qualified_root ()); + ASSERT_NE (nullptr, election); + election->force_confirm (); + } + ASSERT_TIMELY (2s, node1.active.empty () && node1.block_confirmed (send2->hash ())); + // Pruning with default age 1 day + node1.ledger_pruning (1, true, false); + ASSERT_EQ (0, node1.ledger.cache.pruned_count); + ASSERT_EQ (3, node1.ledger.cache.block_count); + // Pruning with max age 0 + node1.config.max_pruning_age = std::chrono::seconds (0); + node1.ledger_pruning (1, true, false); + ASSERT_EQ (1, node1.ledger.cache.pruned_count); + ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_TRUE (node1.ledger.block_exists (genesis.hash ())); + ASSERT_FALSE (node1.ledger.block_exists (send1->hash ())); + ASSERT_TRUE (node1.ledger.block_or_pruned_exists (send1->hash ())); + ASSERT_TRUE (node1.ledger.block_exists (send2->hash ())); +} + +TEST (node, pruning_depth) +{ + nano::system system; + nano::node_config node_config (nano::get_available_port (), system.logging); + node_config.enable_voting = false; // Remove after allowing pruned voting + nano::node_flags node_flags; + node_flags.enable_pruning = true; + auto & node1 = *system.add_node (node_config, node_flags); + nano::genesis genesis; + nano::keypair key1; + auto send1 = nano::send_block_builder () + .previous (genesis.hash ()) + .destination (key1.pub) + .balance (nano::genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) + .work (*system.work.generate (genesis.hash ())) + .build_shared (); + auto send2 = nano::send_block_builder () + .previous (send1->hash ()) + .destination (key1.pub) + .balance (0) + .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build_shared (); + // Process as local blocks + node1.process_active (send1); + node1.process_active (send2); + node1.block_processor.flush (); + // Confirm last block to prune previous + { + auto election = node1.active.election (send1->qualified_root ()); + ASSERT_NE (nullptr, election); + election->force_confirm (); + } + ASSERT_TIMELY (2s, node1.block_confirmed (send1->hash ()) && node1.active.active (send2->qualified_root ())); + ASSERT_EQ (0, node1.ledger.cache.pruned_count); + { + auto election = node1.active.election (send2->qualified_root ()); + ASSERT_NE (nullptr, election); + election->force_confirm (); + } + ASSERT_TIMELY (2s, node1.active.empty () && node1.block_confirmed (send2->hash ())); + // Pruning with default depth (unlimited) + node1.ledger_pruning (1, true, false); + ASSERT_EQ (0, node1.ledger.cache.pruned_count); + ASSERT_EQ (3, node1.ledger.cache.block_count); + // Pruning with max depth 1 + node1.config.max_pruning_depth = 1; + node1.ledger_pruning (1, true, false); + ASSERT_EQ (1, node1.ledger.cache.pruned_count); + ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_TRUE (node1.ledger.block_exists (genesis.hash ())); + ASSERT_FALSE (node1.ledger.block_exists (send1->hash ())); + ASSERT_TRUE (node1.ledger.block_or_pruned_exists (send1->hash ())); + ASSERT_TRUE (node1.ledger.block_exists (send2->hash ())); +} + namespace { void add_required_children_node_config_tree (nano::jsonconfig & tree) diff --git a/nano/lib/errors.cpp b/nano/lib/errors.cpp index 30425b0f..c4223f79 100644 --- a/nano/lib/errors.cpp +++ b/nano/lib/errors.cpp @@ -211,6 +211,8 @@ std::string nano::error_rpc_messages::message (int ev) const return "Invalid threads count"; case nano::error_rpc::peer_not_found: return "Peer not found"; + case nano::error_rpc::pruning_disabled: + return "Pruning is disabled"; case nano::error_rpc::requires_port_and_address: return "Both port and address required"; case nano::error_rpc::rpc_control_disabled: diff --git a/nano/lib/errors.hpp b/nano/lib/errors.hpp index 591352fe..8de203e6 100644 --- a/nano/lib/errors.hpp +++ b/nano/lib/errors.hpp @@ -118,6 +118,7 @@ enum class error_rpc invalid_timestamp, invalid_threads_count, peer_not_found, + pruning_disabled, requires_port_and_address, rpc_control_disabled, sign_hash_disabled, diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 604985c1..147e8ccd 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -102,6 +102,7 @@ int main (int argc, char * const * argv) ("debug_account_versions", "Display the total counts of each version for all accounts (including unpocketed)") ("debug_unconfirmed_frontiers", "Displays the account, height (sorted), frontier and cemented frontier for all accounts which are not fully confirmed") ("validate_blocks,debug_validate_blocks", "Check all blocks for correct hash, signature, work value") + ("debug_prune", "Prune accounts up to last confirmed blocks (EXPERIMENTAL)") ("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 various commands") @@ -1895,6 +1896,15 @@ int main (int argc, char * const * argv) nano::inactive_node node (data_path, node_flags); std::cout << "Total cemented block count: " << node.node->ledger.cache.cemented_count << std::endl; } + else if (vm.count ("debug_prune")) + { + auto node_flags = nano::inactive_node_flag_defaults (); + node_flags.read_only = false; + nano::update_flags (node_flags, vm); + nano::inactive_node inactive_node (data_path, node_flags); + auto node = inactive_node.node; + node->ledger_pruning (node_flags.block_processor_batch_size != 0 ? node_flags.block_processor_batch_size : 16 * 1024, true, true); + } else if (vm.count ("debug_stacktrace")) { std::cout << boost::stacktrace::stacktrace (); diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index c07fef0a..ba761f7e 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -3110,6 +3110,25 @@ void nano::json_handler::process () })); } +void nano::json_handler::pruned_exists () +{ + auto hash (hash_impl ()); + if (!ec) + { + auto transaction (node.store.tx_begin_read ()); + if (node.ledger.pruning) + { + auto exists (node.store.pruned_exists (transaction, hash)); + response_l.put ("exists", exists ? "1" : "0"); + } + else + { + ec = nano::error_rpc::pruning_disabled; + } + } + response_errors (); +} + void nano::json_handler::receive () { auto wallet (wallet_impl ()); @@ -5041,6 +5060,7 @@ ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map () 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 ("pruned_exists", &nano::json_handler::pruned_exists); 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); diff --git a/nano/node/json_handler.hpp b/nano/node/json_handler.hpp index 793131cc..0626bcad 100644 --- a/nano/node/json_handler.hpp +++ b/nano/node/json_handler.hpp @@ -84,6 +84,7 @@ public: void pending (); void pending_exists (); void process (); + void pruned_exists (); void receive (); void receive_minimum (); void receive_minimum_set (); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 08ac0383..da8a47e0 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -650,6 +650,13 @@ void nano::node::start () this_l->ongoing_unchecked_cleanup (); }); } + if (flags.enable_pruning) + { + auto this_l (shared ()); + worker.push_task ([this_l]() { + this_l->ongoing_ledger_pruning (); + }); + } if (!flags.disable_rep_crawler) { rep_crawler.start (); @@ -974,6 +981,124 @@ void nano::node::ongoing_unchecked_cleanup () }); } +bool nano::node::collect_ledger_pruning_targets (std::deque & pruning_targets_a, nano::account & last_account_a, uint64_t const batch_read_size_a, uint64_t const max_depth_a, uint64_t const cutoff_time_a) +{ + uint64_t read_operations (0); + bool finish_transaction (false); + auto transaction (store.tx_begin_read ()); + for (auto i (store.confirmation_height_begin (transaction, last_account_a)), n (store.confirmation_height_end ()); i != n && !finish_transaction;) + { + ++read_operations; + auto const & account (i->first); + nano::block_hash hash (i->second.frontier); + uint64_t depth (0); + while (!hash.is_zero () && depth < max_depth_a) + { + auto block (store.block_get (transaction, hash)); + if (block != nullptr) + { + if (block->sideband ().timestamp > cutoff_time_a || depth == 0) + { + hash = block->previous (); + } + else + { + break; + } + } + else + { + release_assert (depth != 0); + hash = 0; + } + if (++depth % batch_read_size_a == 0) + { + transaction.refresh (); + } + } + if (!hash.is_zero ()) + { + pruning_targets_a.push_back (hash); + } + read_operations += depth; + if (read_operations >= batch_read_size_a) + { + last_account_a = account.number () + 1; + finish_transaction = true; + } + else + { + ++i; + } + } + return !finish_transaction || last_account_a.is_zero (); +} + +void nano::node::ledger_pruning (uint64_t const batch_size_a, bool bootstrap_weight_reached_a, bool log_to_cout_a) +{ + uint64_t const max_depth (config.max_pruning_depth != 0 ? config.max_pruning_depth : std::numeric_limits::max ()); + uint64_t const cutoff_time (bootstrap_weight_reached_a ? nano::seconds_since_epoch () - config.max_pruning_age.count () : std::numeric_limits::max ()); + uint64_t pruned_count (0); + uint64_t transaction_write_count (0); + nano::account last_account (1); // 0 Burn account is never opened. So it can be used to break loop + std::deque pruning_targets; + bool target_finished (false); + while ((transaction_write_count != 0 || !target_finished) && !stopped) + { + // Search pruning targets + while (pruning_targets.size () < batch_size_a && !target_finished && !stopped) + { + target_finished = collect_ledger_pruning_targets (pruning_targets, last_account, batch_size_a * 2, max_depth, cutoff_time); + } + // Pruning write operation + transaction_write_count = 0; + if (!pruning_targets.empty () && !stopped) + { + auto scoped_write_guard = write_database_queue.wait (nano::writer::pruning); + auto write_transaction (store.tx_begin_write ({ tables::blocks, tables::pruned })); + while (!pruning_targets.empty () && transaction_write_count < batch_size_a && !stopped) + { + auto const & pruning_hash (pruning_targets.front ()); + auto account_pruned_count (ledger.pruning_action (write_transaction, pruning_hash, batch_size_a)); + transaction_write_count += account_pruned_count; + pruning_targets.pop_front (); + } + pruned_count += transaction_write_count; + auto log_message (boost::str (boost::format ("%1% blocks pruned") % pruned_count)); + if (!log_to_cout_a) + { + logger.try_log (log_message); + } + else + { + std::cout << log_message << std::endl; + } + } + } + auto log_message (boost::str (boost::format ("Total recently pruned block count: %1%") % pruned_count)); + if (!log_to_cout_a) + { + logger.always_log (log_message); + } + else + { + std::cout << log_message << std::endl; + } +} + +void nano::node::ongoing_ledger_pruning () +{ + auto bootstrap_weight_reached (ledger.cache.block_count >= ledger.bootstrap_weight_max_blocks); + ledger_pruning (flags.block_processor_batch_size != 0 ? flags.block_processor_batch_size : 2 * 1024, bootstrap_weight_reached, false); + auto ledger_pruning_interval (bootstrap_weight_reached ? config.max_pruning_age : std::min (config.max_pruning_age, std::chrono::seconds (15 * 60))); + auto this_l (shared ()); + alarm.add (std::chrono::steady_clock::now () + ledger_pruning_interval, [this_l]() { + this_l->worker.push_task ([this_l]() { + this_l->ongoing_ledger_pruning (); + }); + }); +} + int nano::node::price (nano::uint128_t const & balance_a, int amount_a) { debug_assert (balance_a >= amount_a * nano::Gxrb_ratio); diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 97b04b6a..0aadbcf8 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -132,6 +132,9 @@ public: void search_pending (); void bootstrap_wallet (); void unchecked_cleanup (); + bool collect_ledger_pruning_targets (std::deque &, nano::account &, uint64_t const, uint64_t const, uint64_t const); + void ledger_pruning (uint64_t const, bool, bool); + void ongoing_ledger_pruning (); int price (nano::uint128_t const &, int); // The default difficulty updates to base only when the first epoch_2 block is processed uint64_t default_difficulty (nano::work_version const) const; diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 53875ab0..f61092b1 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -5157,6 +5157,54 @@ TEST (rpc, block_info_pruning) ASSERT_TRUE (response2.json.get ("confirmed")); } +TEST (rpc, pruned_exists) +{ + nano::system system; + auto & node0 = *system.add_node (); + nano::node_config node_config (nano::get_available_port (), system.logging); + node_config.enable_voting = false; // Remove after allowing pruned voting + nano::node_flags node_flags; + node_flags.enable_pruning = true; + auto & node1 = *add_ipc_enabled_node (system, node_config, node_flags); + auto latest (node1.latest (nano::dev_genesis_key.pub)); + auto send1 (std::make_shared (latest, nano::dev_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node1.work_generate_blocking (latest))); + node1.process_active (send1); + auto receive1 (std::make_shared (send1->hash (), send1->hash (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node1.work_generate_blocking (send1->hash ()))); + node1.process_active (receive1); + node1.block_processor.flush (); + system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv); + ASSERT_TIMELY (5s, node1.ledger.cache.cemented_count == 3 && node1.confirmation_height_processor.current ().is_zero () && node1.confirmation_height_processor.awaiting_processing_size () == 0); + // Pruning action + { + auto transaction (node1.store.tx_begin_write ()); + ASSERT_EQ (1, node1.ledger.pruning_action (transaction, send1->hash (), 1)); + } + scoped_io_thread_name_change scoped_thread_name_io; + nano::node_rpc_config node_rpc_config; + nano::ipc::ipc_server ipc_server (node1, node_rpc_config); + nano::rpc_config rpc_config (nano::get_available_port (), true); + rpc_config.rpc_process.ipc_port = node1.config.ipc_config.transport_tcp.port; + 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 (); + // Pruned block + boost::property_tree::ptree request; + request.put ("action", "pruned_exists"); + request.put ("hash", send1->hash ().to_string ()); + test_response response (request, rpc.config.port, system.io_ctx); + ASSERT_TIMELY (5s, response.status != 0); + ASSERT_EQ (200, response.status); + ASSERT_TRUE (response.json.get ("exists")); + // Existing block with previous pruned + boost::property_tree::ptree request2; + request2.put ("action", "pruned_exists"); + request2.put ("hash", receive1->hash ().to_string ()); + test_response response2 (request2, rpc.config.port, system.io_ctx); + ASSERT_TIMELY (5s, response2.status != 0); + ASSERT_EQ (200, response2.status); + ASSERT_FALSE (response2.json.get ("exists")); +} + TEST (rpc, work_peers_all) { nano::system system;