diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/blockchain_utilities/blockchain_export.cpp | 6 | ||||
-rw-r--r-- | src/blockchain_utilities/blockchain_import.cpp | 12 | ||||
-rw-r--r-- | src/blockchain_utilities/bootstrap_file.cpp | 75 | ||||
-rw-r--r-- | src/blockchain_utilities/bootstrap_file.h | 10 | ||||
-rw-r--r-- | src/common/util.cpp | 6 | ||||
-rw-r--r-- | src/cryptonote_config.h | 4 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.cpp | 4 | ||||
-rw-r--r-- | src/cryptonote_protocol/levin_notify.cpp | 35 | ||||
-rw-r--r-- | src/daemon/main.cpp | 3 | ||||
-rw-r--r-- | src/p2p/net_node.inl | 27 | ||||
-rw-r--r-- | src/rpc/bootstrap_daemon.cpp | 10 | ||||
-rw-r--r-- | src/rpc/bootstrap_daemon.h | 3 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 43 | ||||
-rw-r--r-- | src/rpc/rpc_args.cpp | 34 | ||||
-rw-r--r-- | src/rpc/rpc_args.h | 4 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 20 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.h | 1 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 21 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 4 | ||||
-rw-r--r-- | src/wallet/wallet_errors.h | 10 |
20 files changed, 246 insertions, 86 deletions
diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index e55930b00..87cd7945c 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -47,6 +47,7 @@ int main(int argc, char* argv[]) epee::string_tools::set_module_name_and_folder(argv[0]); uint32_t log_level = 0; + uint64_t block_start = 0; uint64_t block_stop = 0; bool blocks_dat = false; @@ -58,6 +59,7 @@ int main(int argc, char* argv[]) po::options_description desc_cmd_sett("Command line options and settings options"); const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true}; const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; + const command_line::arg_descriptor<uint64_t> arg_block_start = {"block-start", "Start at block number", block_start}; const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop}; const command_line::arg_descriptor<bool> arg_blocks_dat = {"blocksdat", "Output in blocks.dat format", blocks_dat}; @@ -67,6 +69,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on); command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on); command_line::add_arg(desc_cmd_sett, arg_log_level); + command_line::add_arg(desc_cmd_sett, arg_block_start); command_line::add_arg(desc_cmd_sett, arg_block_stop); command_line::add_arg(desc_cmd_sett, arg_blocks_dat); @@ -97,6 +100,7 @@ int main(int argc, char* argv[]) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str()); + block_start = command_line::get_arg(vm, arg_block_start); block_stop = command_line::get_arg(vm, arg_block_stop); LOG_PRINT_L0("Starting..."); @@ -178,7 +182,7 @@ int main(int argc, char* argv[]) else { BootstrapFile bootstrap; - r = bootstrap.store_blockchain_raw(core_storage, NULL, output_file_path, block_stop); + r = bootstrap.store_blockchain_raw(core_storage, NULL, output_file_path, block_start, block_stop); } CHECK_AND_ASSERT_MES(r, 1, "Failed to export blockchain raw data"); LOG_PRINT_L0("Blockchain raw data exported OK"); diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index ab2313096..60c069c3b 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -227,6 +227,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path return false; } + uint64_t block_first, block_last; uint64_t start_height = 1, seek_height; if (opt_resume) start_height = core.get_blockchain_storage().get_current_blockchain_height(); @@ -235,10 +236,10 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path BootstrapFile bootstrap; std::streampos pos; // BootstrapFile bootstrap(import_file_path); - uint64_t total_source_blocks = bootstrap.count_blocks(import_file_path, pos, seek_height); - MINFO("bootstrap file last block number: " << total_source_blocks-1 << " (zero-based height) total blocks: " << total_source_blocks); + uint64_t total_source_blocks = bootstrap.count_blocks(import_file_path, pos, seek_height, block_first); + MINFO("bootstrap file last block number: " << total_source_blocks+block_first-1 << " (zero-based height) total blocks: " << total_source_blocks); - if (total_source_blocks-1 <= start_height) + if (total_source_blocks+block_first-1 <= start_height) { return false; } @@ -260,7 +261,8 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path // 4 byte magic + (currently) 1024 byte header structures uint8_t major_version, minor_version; - bootstrap.seek_to_first_chunk(import_file, major_version, minor_version); + uint64_t dummy; + bootstrap.seek_to_first_chunk(import_file, major_version, minor_version, dummy, dummy); std::string str1; char buffer1[1024]; @@ -275,7 +277,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path if (! block_stop) { - block_stop = total_source_blocks - 1; + block_stop = total_source_blocks+block_first - 1; } // These are what we'll try to use, and they don't have to be a determination diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index a4704626f..7050b9ab1 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -52,7 +52,7 @@ namespace -bool BootstrapFile::open_writer(const boost::filesystem::path& file_path) +bool BootstrapFile::open_writer(const boost::filesystem::path& file_path, uint64_t start_block, uint64_t stop_block) { const boost::filesystem::path dir_path = file_path.parent_path(); if (!dir_path.empty()) @@ -78,7 +78,7 @@ bool BootstrapFile::open_writer(const boost::filesystem::path& file_path) m_raw_data_file = new std::ofstream(); bool do_initialize_file = false; - uint64_t num_blocks = 0; + uint64_t num_blocks = 0, block_first = 0; if (! boost::filesystem::exists(file_path)) { @@ -88,10 +88,12 @@ bool BootstrapFile::open_writer(const boost::filesystem::path& file_path) } else { - num_blocks = count_blocks(file_path.string()); - MDEBUG("appending to existing file with height: " << num_blocks-1 << " total blocks: " << num_blocks); + std::streampos dummy_pos; + uint64_t dummy_height = 0; + num_blocks = count_blocks(file_path.string(), dummy_pos, dummy_height, block_first); + MDEBUG("appending to existing file with height: " << num_blocks+block_first-1 << " total blocks: " << num_blocks); } - m_height = num_blocks; + m_height = num_blocks+block_first; if (do_initialize_file) m_raw_data_file->open(file_path.string(), std::ios_base::binary | std::ios_base::out | std::ios::trunc); @@ -106,13 +108,12 @@ bool BootstrapFile::open_writer(const boost::filesystem::path& file_path) return false; if (do_initialize_file) - initialize_file(); + initialize_file(start_block, stop_block); return true; } - -bool BootstrapFile::initialize_file() +bool BootstrapFile::initialize_file(uint64_t first_block, uint64_t last_block) { const uint32_t file_magic = blockchain_raw_magic; @@ -129,8 +130,8 @@ bool BootstrapFile::initialize_file() bfi.header_size = header_size; bootstrap::blocks_info bbi; - bbi.block_first = 0; - bbi.block_last = 0; + bbi.block_first = first_block; + bbi.block_last = last_block; bbi.block_last_pos = 0; buffer_type buffer2; @@ -261,7 +262,7 @@ bool BootstrapFile::close() } -bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop) +bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t start_block, uint64_t requested_block_stop) { uint64_t num_blocks_written = 0; m_max_chunk = 0; @@ -269,17 +270,11 @@ bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem m_tx_pool = _tx_pool; uint64_t progress_interval = 100; MINFO("Storing blocks raw data..."); - if (!BootstrapFile::open_writer(output_file)) - { - MFATAL("failed to open raw file for write"); - return false; - } block b; // block_start, block_stop use 0-based height. m_height uses 1-based height. So to resume export // from last exported block, block_start doesn't need to add 1 here, as it's already at the next // height. - uint64_t block_start = m_height; uint64_t block_stop = 0; MINFO("source blockchain height: " << m_blockchain_storage->get_current_blockchain_height()-1); if ((requested_block_stop > 0) && (requested_block_stop < m_blockchain_storage->get_current_blockchain_height())) @@ -292,6 +287,13 @@ bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem block_stop = m_blockchain_storage->get_current_blockchain_height() - 1; MINFO("Using block height of source blockchain: " << block_stop); } + if (!BootstrapFile::open_writer(output_file, start_block, block_stop)) + { + MFATAL("failed to open raw file for write"); + return false; + } + uint64_t block_start = m_height ? m_height : start_block; + MINFO("Starting block height: " << block_start); for (m_cur_height = block_start; m_cur_height <= block_stop; ++m_cur_height) { // this method's height refers to 0-based height (genesis block = height 0) @@ -323,7 +325,8 @@ bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem return BootstrapFile::close(); } -uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file, uint8_t &major_version, uint8_t &minor_version) +uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file, uint8_t &major_version, uint8_t &minor_version, + uint64_t &block_first, uint64_t &block_last) { uint32_t file_magic; @@ -368,11 +371,35 @@ uint64_t BootstrapFile::seek_to_first_chunk(std::ifstream& import_file, uint8_t MINFO("bootstrap magic size: " << sizeof(file_magic)); MINFO("bootstrap header size: " << bfi.header_size); + uint32_t buflen_blocks_info; + + import_file.read(buf1, sizeof(buflen_blocks_info)); + str1.assign(buf1, sizeof(buflen_blocks_info)); + if (! import_file) + throw std::runtime_error("Error reading expected number of bytes"); + if (! ::serialization::parse_binary(str1, buflen_blocks_info)) + throw std::runtime_error("Error in deserialization of buflen_blocks_info"); + MINFO("bootstrap::blocks_info size: " << buflen_blocks_info); + + if (buflen_blocks_info > sizeof(buf1)) + throw std::runtime_error("Error: bootstrap::blocks_info size exceeds buffer size"); + import_file.read(buf1, buflen_blocks_info); + if (! import_file) + throw std::runtime_error("Error reading expected number of bytes"); + str1.assign(buf1, buflen_blocks_info); + bootstrap::blocks_info bbi; + if (! ::serialization::parse_binary(str1, bbi)) + throw std::runtime_error("Error in deserialization of bootstrap::blocks_info"); + MINFO("bootstrap first block:" << bbi.block_first); + MINFO("bootstrap last block:" << bbi.block_last); + uint64_t full_header_size = sizeof(file_magic) + bfi.header_size; import_file.seekg(full_header_size); major_version = bfi.major_version; minor_version = bfi.minor_version; + block_first = bbi.block_first; + block_last = bbi.block_last; return full_header_size; } @@ -436,13 +463,14 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path) { std::streampos dummy_pos; uint64_t dummy_height = 0; - return count_blocks(import_file_path, dummy_pos, dummy_height); + return count_blocks(import_file_path, dummy_pos, dummy_height, dummy_height); } // If seek_height is non-zero on entry, return a stream position <= this height when finished. // And return the actual height corresponding to this position. Allows the caller to locate its // starting position without having to reread the entire file again. -uint64_t BootstrapFile::count_blocks(const std::string& import_file_path, std::streampos &start_pos, uint64_t& seek_height) +uint64_t BootstrapFile::count_blocks(const std::string& import_file_path, std::streampos &start_pos, + uint64_t& seek_height, uint64_t &block_first) { boost::filesystem::path raw_file_path(import_file_path); boost::system::error_code ec; @@ -464,7 +492,8 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path, std::s uint64_t full_header_size; // 4 byte magic + length of header structures uint8_t major_version, minor_version; - full_header_size = seek_to_first_chunk(import_file, major_version, minor_version); + uint64_t block_last; + full_header_size = seek_to_first_chunk(import_file, major_version, minor_version, block_first, block_last); MINFO("Scanning blockchain from bootstrap file..."); bool quit = false; @@ -473,11 +502,11 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path, std::s while (! quit) { - if (start_height && h + progress_interval >= start_height - 1) + if (start_height && h + block_first + progress_interval >= start_height - 1) { start_height = 0; start_pos = import_file.tellg(); - seek_height = h; + seek_height = h + block_first; } bytes_read += count_bytes(import_file, progress_interval, blocks, quit); h += blocks; diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index 23dbb64d7..ff2875a61 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -57,12 +57,12 @@ class BootstrapFile public: uint64_t count_bytes(std::ifstream& import_file, uint64_t blocks, uint64_t& h, bool& quit); - uint64_t count_blocks(const std::string& dir_path, std::streampos& start_pos, uint64_t& seek_height); + uint64_t count_blocks(const std::string& dir_path, std::streampos& start_pos, uint64_t& seek_height, uint64_t& block_first); uint64_t count_blocks(const std::string& dir_path); - uint64_t seek_to_first_chunk(std::ifstream& import_file, uint8_t &major_version, uint8_t &minor_version); + uint64_t seek_to_first_chunk(std::ifstream& import_file, uint8_t &major_version, uint8_t &minor_version, uint64_t &block_first, uint64_t &block_last); bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp, - boost::filesystem::path& output_file, uint64_t use_block_height=0); + boost::filesystem::path& output_file, uint64_t start_block=0, uint64_t stop_block=0); protected: @@ -75,8 +75,8 @@ protected: boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_type>>* m_output_stream; // open export file for write - bool open_writer(const boost::filesystem::path& file_path); - bool initialize_file(); + bool open_writer(const boost::filesystem::path& file_path, uint64_t start_block, uint64_t stop_block); + bool initialize_file(uint64_t start_block, uint64_t stop_block); bool close(); void write_block(block& block); void flush_chunk(); diff --git a/src/common/util.cpp b/src/common/util.cpp index 433cb4919..f8707d65c 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1000,13 +1000,13 @@ std::string get_nix_version_display_string() for (char c: val) { if (c == '*') - newval += escape ? "*" : ".*"; + newval += escape ? "*" : ".*", escape = false; else if (c == '?') - newval += escape ? "?" : "."; + newval += escape ? "?" : ".", escape = false; else if (c == '\\') newval += '\\', escape = !escape; else - newval += c; + newval += c, escape = false; } return newval; } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index bc17b7e81..826cabe17 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -102,11 +102,11 @@ #define CRYPTONOTE_DANDELIONPP_STEMS 2 // number of outgoing stem connections per epoch -#define CRYPTONOTE_DANDELIONPP_FLUFF_PROBABILITY 10 // out of 100 +#define CRYPTONOTE_DANDELIONPP_FLUFF_PROBABILITY 20 // out of 100 #define CRYPTONOTE_DANDELIONPP_MIN_EPOCH 10 // minutes #define CRYPTONOTE_DANDELIONPP_EPOCH_RANGE 30 // seconds #define CRYPTONOTE_DANDELIONPP_FLUSH_AVERAGE 5 // seconds average for poisson distributed fluff flush -#define CRYPTONOTE_DANDELIONPP_EMBARGO_AVERAGE 173 // seconds (see tx_pool.cpp for more info) +#define CRYPTONOTE_DANDELIONPP_EMBARGO_AVERAGE 39 // seconds (see tx_pool.cpp for more info) // see src/cryptonote_protocol/levin_notify.cpp #define CRYPTONOTE_NOISE_MIN_EPOCH 5 // minutes diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 0db133b62..d059ab78f 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -75,11 +75,11 @@ namespace cryptonote not ideal since a blackhole is more likely to reveal earlier nodes in the chain. - This value was calculated with k=10, ep=0.10, and hop = 175 ms. A + This value was calculated with k=5, ep=0.10, and hop = 175 ms. A testrun from a recent Intel laptop took ~80ms to receive+parse+proces+send transaction. At least 50ms will be added to the latency if crossing an ocean. So 175ms is the fudge factor for - a single hop with 173s being the embargo timer. */ + a single hop with 39s being the embargo timer. */ constexpr const std::chrono::seconds dandelionpp_embargo_average{CRYPTONOTE_DANDELIONPP_EMBARGO_AVERAGE}; //TODO: constants such as these should at least be in the header, diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index 75ba68d3b..2d04dffb6 100644 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -283,9 +283,10 @@ namespace levin strand(io_service), map(), channels(), - flush_time(std::chrono::steady_clock::time_point::max()), connection_count(0), + flush_callbacks(0), nzone(zone), + is_public(is_public), pad_txs(pad_txs), fluffing(false) { @@ -300,9 +301,10 @@ namespace levin boost::asio::io_service::strand strand; net::dandelionpp::connection_map map;//!< Tracks outgoing uuid's for noise channels or Dandelion++ stems std::deque<noise_channel> channels; //!< Never touch after init; only update elements on `noise_channel.strand` - std::chrono::steady_clock::time_point flush_time; //!< Next expected Dandelion++ fluff flush std::atomic<std::size_t> connection_count; //!< Only update in strand, can be read at any time + std::uint32_t flush_callbacks; //!< Number of active fluff flush callbacks queued const epee::net_utils::zone nzone; //!< Zone is public ipv4/ipv6 connections, or i2p or tor + const bool is_public; //!< Zone is public ipv4/ipv6 connections const bool pad_txs; //!< Pad txs to the next boundary for privacy bool fluffing; //!< Zone is in Dandelion++ fluff epoch }; @@ -348,7 +350,6 @@ namespace levin struct fluff_flush { std::shared_ptr<detail::zone> zone_; - std::chrono::steady_clock::time_point flush_time_; static void queue(std::shared_ptr<detail::zone> zone, const std::chrono::steady_clock::time_point flush_time) { @@ -356,28 +357,21 @@ namespace levin assert(zone->strand.running_in_this_thread()); detail::zone& this_zone = *zone; - this_zone.flush_time = flush_time; + ++this_zone.flush_callbacks; this_zone.flush_txs.expires_at(flush_time); - this_zone.flush_txs.async_wait(this_zone.strand.wrap(fluff_flush{std::move(zone), flush_time})); + this_zone.flush_txs.async_wait(this_zone.strand.wrap(fluff_flush{std::move(zone)})); } void operator()(const boost::system::error_code error) { - if (!zone_ || !zone_->p2p) + if (!zone_ || !zone_->flush_callbacks || --zone_->flush_callbacks || !zone_->p2p) return; assert(zone_->strand.running_in_this_thread()); const bool timer_error = bool(error); - if (timer_error) - { - if (error != boost::system::errc::operation_canceled) - throw boost::system::system_error{error, "fluff_flush timer failed"}; - - // new timer canceled this one set in future - if (zone_->flush_time < flush_time_) - return; - } + if (timer_error && error != boost::system::errc::operation_canceled) + throw boost::system::system_error{error, "fluff_flush timer failed"}; const auto now = std::chrono::steady_clock::now(); auto next_flush = std::chrono::steady_clock::time_point::max(); @@ -413,8 +407,6 @@ namespace levin if (next_flush != std::chrono::steady_clock::time_point::max()) fluff_flush::queue(std::move(zone_), next_flush); - else - zone_->flush_time = next_flush; // signal that no timer is set } }; @@ -449,13 +441,11 @@ namespace levin MDEBUG("Queueing " << txs.size() << " transaction(s) for Dandelion++ fluffing"); - bool available = false; - zone->p2p->foreach_connection([txs, now, &zone, &source, &in_duration, &out_duration, &next_flush, &available] (detail::p2p_context& context) + zone->p2p->foreach_connection([txs, now, &zone, &source, &in_duration, &out_duration, &next_flush] (detail::p2p_context& context) { // When i2p/tor, only fluff to outbound connections if (source != context.m_connection_id && (zone->nzone == epee::net_utils::zone::public_ || !context.m_is_income)) { - available = true; if (context.fluff_txs.empty()) context.flush_time = now + (context.m_is_income ? in_duration() : out_duration()); @@ -467,10 +457,9 @@ namespace levin return true; }); - if (!available) + if (next_flush == std::chrono::steady_clock::time_point::max()) MWARNING("Unable to send transaction(s), no available connections"); - - if (next_flush < zone->flush_time) + else if (!zone->flush_callbacks || next_flush < zone->flush_txs.expires_at()) fluff_flush::queue(std::move(zone), next_flush); } }; diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 3db8fbcb4..d413906df 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -66,10 +66,12 @@ uint16_t parse_public_rpc_port(const po::variables_map &vm) } std::string rpc_port_str; + std::string rpc_bind_address = command_line::get_arg(vm, cryptonote::rpc_args::descriptors().rpc_bind_ip); const auto &restricted_rpc_port = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port; if (!command_line::is_arg_defaulted(vm, restricted_rpc_port)) { rpc_port_str = command_line::get_arg(vm, restricted_rpc_port); + rpc_bind_address = command_line::get_arg(vm, cryptonote::rpc_args::descriptors().rpc_restricted_bind_ip); } else if (command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc)) { @@ -86,7 +88,6 @@ uint16_t parse_public_rpc_port(const po::variables_map &vm) throw std::runtime_error("invalid RPC port " + rpc_port_str); } - const auto rpc_bind_address = command_line::get_arg(vm, cryptonote::rpc_args::descriptors().rpc_bind_ip); const auto address = net::get_network_address(rpc_bind_address, rpc_port); if (!address) { throw std::runtime_error("failed to parse RPC bind address"); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 64c740a22..5ef40218d 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1514,13 +1514,28 @@ namespace nodetool }); } + auto get_host_string = [](const epee::net_utils::network_address &address) { +#if BOOST_VERSION > 106600 + if (address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()) + { + boost::asio::ip::address_v6 actual_ip = address.as<const epee::net_utils::ipv6_network_address>().ip(); + if (actual_ip.is_v4_mapped()) + { + boost::asio::ip::address_v4 v4ip = make_address_v4(boost::asio::ip::v4_mapped, actual_ip); + return epee::net_utils::ipv4_network_address(v4ip.to_uint(), 0).host_str(); + } + } +#endif + return address.host_str(); + }; + std::unordered_set<std::string> hosts_added; std::deque<size_t> filtered; const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max(); for (int step = 0; step < 2; ++step) { bool skip_duplicate_class_B = step == 0; size_t idx = 0, skipped = 0; - zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){ + zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe, &hosts_added, &get_host_string](const peerlist_entry &pe){ if (filtered.size() >= limit) return false; bool skip = false; @@ -1544,6 +1559,15 @@ namespace nodetool } } #endif + + // consider each host once, to avoid giving undue inflence to hosts running several nodes + if (!skip) + { + const auto i = hosts_added.find(get_host_string(pe.adr)); + if (i != hosts_added.end()) + skip = true; + } + if (skip) ++skipped; else if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0) @@ -1551,6 +1575,7 @@ namespace nodetool else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed)) filtered.push_front(idx); ++idx; + hosts_added.insert(get_host_string(pe.adr)); return true; }); if (skipped == 0 || !filtered.empty()) diff --git a/src/rpc/bootstrap_daemon.cpp b/src/rpc/bootstrap_daemon.cpp index 6a0833f19..2fdd28406 100644 --- a/src/rpc/bootstrap_daemon.cpp +++ b/src/rpc/bootstrap_daemon.cpp @@ -45,12 +45,12 @@ namespace cryptonote return host + ":" + m_http_client.get_port(); } - boost::optional<uint64_t> bootstrap_daemon::get_height() + boost::optional<std::pair<uint64_t, uint64_t>> bootstrap_daemon::get_height() { - cryptonote::COMMAND_RPC_GET_HEIGHT::request req; - cryptonote::COMMAND_RPC_GET_HEIGHT::response res; + cryptonote::COMMAND_RPC_GET_INFO::request req; + cryptonote::COMMAND_RPC_GET_INFO::response res; - if (!invoke_http_json("/getheight", req, res)) + if (!invoke_http_json("/getinfo", req, res)) { return boost::none; } @@ -60,7 +60,7 @@ namespace cryptonote return boost::none; } - return res.height; + return {{res.height, res.target_height}}; } bool bootstrap_daemon::handle_result(bool success, const std::string &status) diff --git a/src/rpc/bootstrap_daemon.h b/src/rpc/bootstrap_daemon.h index bedc255b5..d54042b11 100644 --- a/src/rpc/bootstrap_daemon.h +++ b/src/rpc/bootstrap_daemon.h @@ -2,6 +2,7 @@ #include <functional> #include <map> +#include <utility> #include <boost/optional/optional.hpp> #include <boost/thread/mutex.hpp> @@ -27,7 +28,7 @@ namespace cryptonote bool rpc_payment_enabled); std::string address() const noexcept; - boost::optional<uint64_t> get_height(); + boost::optional<std::pair<uint64_t, uint64_t>> get_height(); bool handle_result(bool success, const std::string &status); template <class t_request, class t_response> diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index fc67e2dd1..98e8e8c66 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -264,6 +264,18 @@ namespace cryptonote if (!rpc_config) return false; + std::string bind_ip_str = rpc_config->bind_ip; + std::string bind_ipv6_str = rpc_config->bind_ipv6_address; + if (restricted) + { + const auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port; + const bool has_restricted_rpc_port_arg = !command_line::is_arg_defaulted(vm, restricted_rpc_port_arg); + if (has_restricted_rpc_port_arg && port == command_line::get_arg(vm, restricted_rpc_port_arg)) + { + bind_ip_str = rpc_config->restricted_bind_ip; + bind_ipv6_str = rpc_config->restricted_bind_ipv6_address; + } + } disable_rpc_ban = rpc_config->disable_rpc_ban; std::string address = command_line::get_arg(vm, arg_rpc_payment_address); if (!address.empty() && allow_rpc_payment) @@ -300,7 +312,7 @@ namespace cryptonote if (!m_rpc_payment) { uint32_t bind_ip; - bool ok = epee::string_tools::get_ip_int32_from_string(bind_ip, rpc_config->bind_ip); + bool ok = epee::string_tools::get_ip_int32_from_string(bind_ip, bind_ip_str); if (ok & !epee::net_utils::is_ip_loopback(bind_ip)) MWARNING("The RPC server is accessible from the outside, but no RPC payment was setup. RPC access will be free for all."); } @@ -322,8 +334,8 @@ namespace cryptonote auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); }; return epee::http_server_impl_base<core_rpc_server, connection_context>::init( - rng, std::move(port), std::move(rpc_config->bind_ip), - std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4), + rng, std::move(port), std::move(bind_ip_str), + std::move(bind_ipv6_str), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4), std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options) ); } @@ -1991,34 +2003,37 @@ namespace cryptonote } auto current_time = std::chrono::system_clock::now(); - if (!m_p2p.get_payload_object().no_sync() && - current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s + if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s { { boost::upgrade_to_unique_lock<boost::shared_mutex> lock(upgrade_lock); m_bootstrap_height_check_time = current_time; } - boost::optional<uint64_t> bootstrap_daemon_height = m_bootstrap_daemon->get_height(); - if (!bootstrap_daemon_height) + boost::optional<std::pair<uint64_t, uint64_t>> bootstrap_daemon_height_info = m_bootstrap_daemon->get_height(); + if (!bootstrap_daemon_height_info) { MERROR("Failed to fetch bootstrap daemon height"); return false; } - uint64_t target_height = m_core.get_target_blockchain_height(); - if (*bootstrap_daemon_height < target_height) + const uint64_t bootstrap_daemon_height = bootstrap_daemon_height_info->first; + const uint64_t bootstrap_daemon_target_height = bootstrap_daemon_height_info->second; + if (bootstrap_daemon_height < bootstrap_daemon_target_height) { MINFO("Bootstrap daemon is out of sync"); return m_bootstrap_daemon->handle_result(false, {}); } - uint64_t top_height = m_core.get_current_blockchain_height(); - m_should_use_bootstrap_daemon = top_height + 10 < *bootstrap_daemon_height; - MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << *bootstrap_daemon_height << ")"); + if (!m_p2p.get_payload_object().no_sync()) + { + uint64_t top_height = m_core.get_current_blockchain_height(); + m_should_use_bootstrap_daemon = top_height + 10 < bootstrap_daemon_height; + MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << bootstrap_daemon_height << ")"); - if (!m_should_use_bootstrap_daemon) - return false; + if (!m_should_use_bootstrap_daemon) + return false; + } } if (mode == invoke_http_mode::JON) diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index 8601bd0b4..0966fb6d2 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -91,6 +91,8 @@ namespace cryptonote rpc_args::descriptors::descriptors() : rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify IP to bind RPC server"), "127.0.0.1"}) , rpc_bind_ipv6_address({"rpc-bind-ipv6-address", rpc_args::tr("Specify IPv6 address to bind RPC server"), "::1"}) + , rpc_restricted_bind_ip({"rpc-restricted-bind-ip", rpc_args::tr("Specify IP to bind restricted RPC server"), "127.0.0.1"}) + , rpc_restricted_bind_ipv6_address({"rpc-restricted-bind-ipv6-address", rpc_args::tr("Specify IPv6 address to bind restricted RPC server"), "::1"}) , rpc_use_ipv6({"rpc-use-ipv6", rpc_args::tr("Allow IPv6 for RPC"), false}) , rpc_ignore_ipv4({"rpc-ignore-ipv4", rpc_args::tr("Ignore unsuccessful IPv4 bind for RPC"), false}) , rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true}) @@ -113,6 +115,8 @@ namespace cryptonote const descriptors arg{}; command_line::add_arg(desc, arg.rpc_bind_ip); command_line::add_arg(desc, arg.rpc_bind_ipv6_address); + command_line::add_arg(desc, arg.rpc_restricted_bind_ip); + command_line::add_arg(desc, arg.rpc_restricted_bind_ipv6_address); command_line::add_arg(desc, arg.rpc_use_ipv6); command_line::add_arg(desc, arg.rpc_ignore_ipv4); command_line::add_arg(desc, arg.rpc_login); @@ -136,6 +140,8 @@ namespace cryptonote config.bind_ip = command_line::get_arg(vm, arg.rpc_bind_ip); config.bind_ipv6_address = command_line::get_arg(vm, arg.rpc_bind_ipv6_address); + config.restricted_bind_ip = command_line::get_arg(vm, arg.rpc_restricted_bind_ip); + config.restricted_bind_ipv6_address = command_line::get_arg(vm, arg.rpc_restricted_bind_ipv6_address); config.use_ipv6 = command_line::get_arg(vm, arg.rpc_use_ipv6); config.require_ipv4 = !command_line::get_arg(vm, arg.rpc_ignore_ipv4); config.disable_rpc_ban = command_line::get_arg(vm, arg.disable_rpc_ban); @@ -188,6 +194,34 @@ namespace cryptonote return boost::none; } } + if (!config.restricted_bind_ip.empty()) + { + // always parse IP here for error consistency + boost::system::error_code ec{}; + boost::asio::ip::address::from_string(config.restricted_bind_ip, ec); + if (ec) + { + LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_restricted_bind_ip.name); + return boost::none; + } + } + if (!config.restricted_bind_ipv6_address.empty()) + { + // allow square braces, but remove them here if present + if (config.restricted_bind_ipv6_address.find('[') != std::string::npos) + { + config.restricted_bind_ipv6_address = config.restricted_bind_ipv6_address.substr(1, config.restricted_bind_ipv6_address.size() - 2); + } + + // always parse IP here for error consistency + boost::system::error_code ec{}; + boost::asio::ip::address::from_string(config.restricted_bind_ipv6_address, ec); + if (ec) + { + LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_restricted_bind_ipv6_address.name); + return boost::none; + } + } const char *env_rpc_login = nullptr; const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login); diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h index 4cf4279b2..f06e539bd 100644 --- a/src/rpc/rpc_args.h +++ b/src/rpc/rpc_args.h @@ -53,6 +53,8 @@ namespace cryptonote const command_line::arg_descriptor<std::string> rpc_bind_ip; const command_line::arg_descriptor<std::string> rpc_bind_ipv6_address; + const command_line::arg_descriptor<std::string> rpc_restricted_bind_ip; + const command_line::arg_descriptor<std::string> rpc_restricted_bind_ipv6_address; const command_line::arg_descriptor<bool> rpc_use_ipv6; const command_line::arg_descriptor<bool> rpc_ignore_ipv4; const command_line::arg_descriptor<std::string> rpc_login; @@ -81,6 +83,8 @@ namespace cryptonote std::string bind_ip; std::string bind_ipv6_address; + std::string restricted_bind_ip; + std::string restricted_bind_ipv6_address; bool use_ipv6; bool require_ipv4; std::vector<std::string> access_control_origins; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 7b26d21fb..a90bd3a39 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2705,6 +2705,24 @@ bool simple_wallet::set_unit(const std::vector<std::string> &args/* = std::vecto return true; } +bool simple_wallet::set_max_reorg_depth(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + uint64_t depth; + if (!epee::string_tools::get_xtype_from_string(depth, args[1])) + { + fail_msg_writer() << tr("invalid value"); + return true; + } + + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + m_wallet->max_reorg_depth(depth); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + return true; +} + bool simple_wallet::set_min_output_count(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { uint32_t count; @@ -3783,6 +3801,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) success_msg_writer() << "priority = " << priority<< " (" << priority_string << ")"; success_msg_writer() << "ask-password = " << m_wallet->ask_password() << " (" << ask_password_string << ")"; success_msg_writer() << "unit = " << cryptonote::get_unit(cryptonote::get_default_decimal_point()); + success_msg_writer() << "max-reorg-depth = " << m_wallet->max_reorg_depth(); success_msg_writer() << "min-outputs-count = " << m_wallet->get_min_output_count(); success_msg_writer() << "min-outputs-value = " << cryptonote::print_money(m_wallet->get_min_output_value()); success_msg_writer() << "merge-destinations = " << m_wallet->merge_destinations(); @@ -3853,6 +3872,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4, or one of ") << join_priority_strings(", ")); CHECK_SIMPLE_VARIABLE("ask-password", set_ask_password, tr("0|1|2 (or never|action|decrypt)")); CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("monero, millinero, micronero, nanonero, piconero")); + CHECK_SIMPLE_VARIABLE("max-reorg-depth", set_max_reorg_depth, tr("unsigned integer")); CHECK_SIMPLE_VARIABLE("min-outputs-count", set_min_output_count, tr("unsigned integer")); CHECK_SIMPLE_VARIABLE("min-outputs-value", set_min_output_value, tr("amount")); CHECK_SIMPLE_VARIABLE("merge-destinations", set_merge_destinations, tr("0 or 1")); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 5846fe056..61104c87f 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -131,6 +131,7 @@ namespace cryptonote bool set_confirm_missing_payment_id(const std::vector<std::string> &args = std::vector<std::string>()); bool set_ask_password(const std::vector<std::string> &args = std::vector<std::string>()); bool set_unit(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_max_reorg_depth(const std::vector<std::string> &args = std::vector<std::string>()); bool set_min_output_count(const std::vector<std::string> &args = std::vector<std::string>()); bool set_min_output_value(const std::vector<std::string> &args = std::vector<std::string>()); bool set_merge_destinations(const std::vector<std::string> &args = std::vector<std::string>()); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7cbb4a910..30d06c20c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1158,6 +1158,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std m_explicit_refresh_from_block_height(true), m_confirm_non_default_ring_size(true), m_ask_password(AskPasswordToDecrypt), + m_max_reorg_depth(ORPHANED_BLOCKS_MAX_COUNT), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), @@ -3469,6 +3470,15 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo throw std::runtime_error("proxy exception in refresh thread"); } + if (!next_blocks.empty()) + { + const uint64_t expected_start_height = std::max(static_cast<uint64_t>(m_blockchain.size()), uint64_t(1)) - 1; + const uint64_t reorg_depth = expected_start_height - std::min(expected_start_height, next_blocks_start_height); + THROW_WALLET_EXCEPTION_IF(reorg_depth > m_max_reorg_depth, error::reorg_depth_error, + tr("reorg exceeds maximum allowed depth, use 'set max-reorg-depth N' to allow it, reorg depth: ") + + std::to_string(reorg_depth)); + } + // if we've got at least 10 blocks to refresh, assume we're starting // a long refresh, and setup a tracking output cache if we need to if (m_track_uses && (!output_tracker_cache || output_tracker_cache->empty()) && next_blocks.size() >= 10) @@ -3491,6 +3501,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool"); throw; } + catch (const error::reorg_depth_error&) + { + THROW_WALLET_EXCEPTION_IF(!waiter.wait(), error::wallet_internal_error, "Exception in thread pool"); + throw; + } catch (const std::exception&) { blocks_fetched += added_blocks; @@ -3867,6 +3882,9 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee: value2.SetInt(m_ask_password); json.AddMember("ask_password", value2, json.GetAllocator()); + value2.SetUint64(m_max_reorg_depth); + json.AddMember("max_reorg_depth", value2, json.GetAllocator()); + value2.SetUint(m_min_output_count); json.AddMember("min_output_count", value2, json.GetAllocator()); @@ -4085,6 +4103,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st m_confirm_non_default_ring_size = true; m_ask_password = AskPasswordToDecrypt; cryptonote::set_default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT); + m_max_reorg_depth = ORPHANED_BLOCKS_MAX_COUNT; m_min_output_count = 0; m_min_output_value = 0; m_merge_destinations = false; @@ -4237,6 +4256,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st m_ask_password = field_ask_password; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT); cryptonote::set_default_decimal_point(field_default_decimal_point); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, max_reorg_depth, uint64_t, Uint64, false, ORPHANED_BLOCKS_MAX_COUNT); + m_max_reorg_depth = field_max_reorg_depth; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_count, uint32_t, Uint, false, 0); m_min_output_count = field_min_output_count; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_value, uint64_t, Uint64, false, 0); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index fed7d745c..68f03db72 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -855,6 +855,9 @@ private: void explicit_refresh_from_block_height(bool expl) {m_explicit_refresh_from_block_height = expl;} bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;} + void max_reorg_depth(uint64_t depth) {m_max_reorg_depth = depth;} + uint64_t max_reorg_depth() const {return m_max_reorg_depth;} + bool deinit(); bool init(std::string daemon_address = "http://localhost:8080", boost::optional<epee::net_utils::http::login> daemon_login = boost::none, @@ -1728,6 +1731,7 @@ private: bool m_explicit_refresh_from_block_height; bool m_confirm_non_default_ring_size; AskPasswordType m_ask_password; + uint64_t m_max_reorg_depth; uint32_t m_min_output_count; uint64_t m_min_output_value; bool m_merge_destinations; diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index e889ed7d1..4a89ed81a 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -428,6 +428,16 @@ namespace tools std::string to_string() const { return refresh_error::to_string(); } }; //---------------------------------------------------------------------------------------------------- + struct reorg_depth_error : public refresh_error + { + explicit reorg_depth_error(std::string&& loc, const std::string& message) + : refresh_error(std::move(loc), message) + { + } + + std::string to_string() const { return refresh_error::to_string(); } + }; + //---------------------------------------------------------------------------------------------------- struct signature_check_failed : public wallet_logic_error { explicit signature_check_failed(std::string&& loc, const std::string& message) |