aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_utilities/blockchain_export.cpp6
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp12
-rw-r--r--src/blockchain_utilities/bootstrap_file.cpp75
-rw-r--r--src/blockchain_utilities/bootstrap_file.h10
-rw-r--r--src/common/util.cpp6
-rw-r--r--src/cryptonote_config.h4
-rw-r--r--src/cryptonote_core/tx_pool.cpp4
-rw-r--r--src/cryptonote_protocol/levin_notify.cpp35
-rw-r--r--src/daemon/main.cpp3
-rw-r--r--src/p2p/net_node.inl27
-rw-r--r--src/rpc/bootstrap_daemon.cpp10
-rw-r--r--src/rpc/bootstrap_daemon.h3
-rw-r--r--src/rpc/core_rpc_server.cpp43
-rw-r--r--src/rpc/rpc_args.cpp34
-rw-r--r--src/rpc/rpc_args.h4
-rw-r--r--src/simplewallet/simplewallet.cpp20
-rw-r--r--src/simplewallet/simplewallet.h1
-rw-r--r--src/wallet/wallet2.cpp21
-rw-r--r--src/wallet/wallet2.h4
-rw-r--r--src/wallet/wallet_errors.h10
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)