mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
310 lines
12 KiB
C++
310 lines
12 KiB
C++
// Copyright (c) 2018, The Loki Project
|
|
// Copyright (c) 2014-2019, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
|
|
|
#include "util.h"
|
|
|
|
#include <fmt/chrono.h>
|
|
#include <fmt/color.h>
|
|
|
|
#include <chrono>
|
|
#include <iomanip>
|
|
#include <string>
|
|
#include <thread>
|
|
|
|
#include "crypto/crypto.h"
|
|
#include "epee/misc_log_ex.h"
|
|
#include "epee/readline_buffer.h"
|
|
#include "epee/string_tools.h"
|
|
#include "epee/wipeable_string.h"
|
|
#include "i18n.h"
|
|
#include "logging/oxen_logger.h"
|
|
#include "string_util.h"
|
|
|
|
#ifdef __GLIBC__
|
|
#include <gnu/libc-version.h>
|
|
#include <sys/resource.h>
|
|
#endif
|
|
|
|
namespace tools {
|
|
static auto logcat = log::Cat("util");
|
|
|
|
bool disable_core_dumps() {
|
|
#ifdef __GLIBC__
|
|
// disable core dumps in release mode
|
|
struct rlimit rlimit;
|
|
rlimit.rlim_cur = rlimit.rlim_max = 0;
|
|
if (setrlimit(RLIMIT_CORE, &rlimit)) {
|
|
log::warning(logcat, "Failed to disable core dumps");
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
ssize_t get_lockable_memory() {
|
|
#ifdef __GLIBC__
|
|
struct rlimit rlim;
|
|
if (getrlimit(RLIMIT_MEMLOCK, &rlim) < 0) {
|
|
log::error(logcat, "Failed to determine the lockable memory limit");
|
|
return -1;
|
|
}
|
|
return rlim.rlim_cur;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
bool on_startup() {
|
|
#ifdef __GLIBC__
|
|
const char* ver = ::gnu_get_libc_version();
|
|
if (!strcmp(ver, "2.25"))
|
|
log::warning(
|
|
logcat,
|
|
fg(fmt::terminal_color::red),
|
|
"Running with glibc {}, hangs may occur - change glibc version if possible",
|
|
ver);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
namespace {
|
|
std::mutex max_concurrency_lock;
|
|
unsigned max_concurrency = std::thread::hardware_concurrency();
|
|
} // namespace
|
|
|
|
void set_max_concurrency(unsigned n) {
|
|
if (n < 1)
|
|
n = std::thread::hardware_concurrency();
|
|
unsigned hwc = std::thread::hardware_concurrency();
|
|
if (n > hwc)
|
|
n = hwc;
|
|
std::lock_guard lock{max_concurrency_lock};
|
|
max_concurrency = n;
|
|
}
|
|
|
|
unsigned get_max_concurrency() {
|
|
std::lock_guard lock{max_concurrency_lock};
|
|
return max_concurrency;
|
|
}
|
|
|
|
bool is_local_address(const std::string& address) {
|
|
return address == "localhost"sv ||
|
|
(tools::starts_with(address, "127."sv) &&
|
|
address.find_first_not_of("0123456789."sv) == std::string::npos) ||
|
|
address == "::1"sv ||
|
|
address == "[::1]"sv; // There are other uncommon ways to specify localhost (e.g. 0::1,
|
|
// ::0001) but don't worry about them.
|
|
}
|
|
|
|
int vercmp(std::string_view v0, std::string_view v1) {
|
|
auto f0 = tools::split_any(v0, ".-");
|
|
auto f1 = tools::split_any(v1, ".-");
|
|
const auto max = std::max(f0.size(), f1.size());
|
|
for (size_t i = 0; i < max; ++i) {
|
|
if (i >= f0.size())
|
|
return -1;
|
|
if (i >= f1.size())
|
|
return 1;
|
|
int f0i = 0, f1i = 0;
|
|
tools::parse_int(f0[i], f0i);
|
|
tools::parse_int(f1[i], f1i);
|
|
int n = f0i - f1i;
|
|
if (n)
|
|
return n;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
std::optional<std::pair<uint32_t, uint32_t>> parse_subaddress_lookahead(const std::string& str) {
|
|
auto pos = str.find(":");
|
|
bool r = pos != std::string::npos;
|
|
uint32_t major;
|
|
r = r && epee::string_tools::get_xtype_from_string(major, str.substr(0, pos));
|
|
uint32_t minor;
|
|
r = r && epee::string_tools::get_xtype_from_string(minor, str.substr(pos + 1));
|
|
if (r) {
|
|
return std::make_pair(major, minor);
|
|
} else {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
std::string input_line_win() {
|
|
HANDLE hConIn = CreateFileW(
|
|
L"CONIN$",
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
nullptr,
|
|
OPEN_EXISTING,
|
|
0,
|
|
nullptr);
|
|
DWORD oldMode;
|
|
|
|
FlushConsoleInputBuffer(hConIn);
|
|
GetConsoleMode(hConIn, &oldMode);
|
|
SetConsoleMode(hConIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);
|
|
|
|
wchar_t buffer[1024];
|
|
DWORD read;
|
|
|
|
ReadConsoleW(hConIn, buffer, sizeof(buffer) / sizeof(wchar_t) - 1, &read, nullptr);
|
|
buffer[read] = 0;
|
|
|
|
SetConsoleMode(hConIn, oldMode);
|
|
CloseHandle(hConIn);
|
|
|
|
int size_needed = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL);
|
|
std::string buf(size_needed, '\0');
|
|
WideCharToMultiByte(CP_UTF8, 0, buffer, -1, &buf[0], size_needed, NULL, NULL);
|
|
buf.pop_back(); // size_needed includes null that we needed to have space for
|
|
return buf;
|
|
}
|
|
#endif
|
|
|
|
std::string get_human_readable_timestamp(std::time_t t) {
|
|
if (t < 1234567890)
|
|
return "<unknown>";
|
|
return "{:%Y-%m-%d %H:%M:%S} UTC"_format(fmt::gmtime(t));
|
|
}
|
|
|
|
std::string get_human_readable_timespan(std::chrono::seconds seconds) {
|
|
uint64_t ts = seconds.count();
|
|
if (ts < 60)
|
|
return std::to_string(ts) + tr(" seconds");
|
|
if (ts < 3600)
|
|
return std::to_string((uint64_t)(ts / 60)) + tr(" minutes");
|
|
if (ts < 3600 * 24)
|
|
return std::to_string((uint64_t)(ts / 3600)) + tr(" hours");
|
|
if (ts < 3600 * 24 * 30.5)
|
|
return std::to_string((uint64_t)(ts / (3600 * 24))) + tr(" days");
|
|
if (ts < 3600 * 24 * 365.25)
|
|
return std::to_string((uint64_t)(ts / (3600 * 24 * 30.5))) + tr(" months");
|
|
return tr("a long time");
|
|
}
|
|
|
|
std::string get_human_readable_bytes(uint64_t bytes) {
|
|
if (bytes < 1000)
|
|
return std::to_string(bytes) + " B";
|
|
constexpr std::array units{" kB", " MB", " GB", " TB"};
|
|
double b = bytes;
|
|
for (const auto& suffix : units) {
|
|
b /= 1000.;
|
|
if (b < 1000.) {
|
|
std::ostringstream o;
|
|
o << std::fixed << std::setprecision(2) << b;
|
|
return o.str() + suffix;
|
|
}
|
|
}
|
|
return std::to_string(std::lround(b)) + units.back();
|
|
}
|
|
|
|
// Calculate a "sync weight" over ranges of blocks in the blockchain, suitable for
|
|
// calculating sync time estimates
|
|
uint64_t cumulative_block_sync_weight(
|
|
cryptonote::network_type nettype, uint64_t start_block, uint64_t num_blocks) {
|
|
// No detailed data available except for Mainnet: Give back the number of blocks
|
|
// as a very simple and non-varying block sync weight for ranges of Testnet and
|
|
// Devnet blocks
|
|
return num_blocks;
|
|
|
|
// TODO(oxen):
|
|
#if 0
|
|
// The following is a table of average blocks sizes in bytes over the Monero mainnet
|
|
// blockchain, where the block size is averaged over ranges of 10,000 blocks
|
|
// (about 2 weeks worth of blocks each).
|
|
// The first array entry of 442 thus means "The average byte size of the blocks
|
|
// 0 .. 9,999 is 442". The info "block_size" from the "get_block_header_by_height"
|
|
// RPC call was used for calculating this. This table (and the whole mechanism
|
|
// of calculating a "sync weight") is most important when estimating times for
|
|
// syncing from scratch. Without it the fast progress through the (in comparison)
|
|
// rather small blocks in the early blockchain) would lead to vastly underestimated
|
|
// total sync times.
|
|
// It's no big problem for estimates that this table will, over time, and if not
|
|
// updated, miss larger and larger parts at the top of the blockchain, as long
|
|
// as block size averages there do not differ wildly.
|
|
// Without time-consuming tests it's hard to say how much the estimates would
|
|
// improve if one would not only take block sizes into account, but also varying
|
|
// verification times i.e. the different CPU effort needed for the different
|
|
// transaction types (pre / post RingCT, pre / post Bulletproofs).
|
|
// Testnet and Devnet are neglected here because of their much smaller
|
|
// importance.
|
|
static const uint32_t average_block_sizes[] =
|
|
{
|
|
442, 1211, 1445, 1763, 2272, 8217, 5603, 9999, 16358, 10805, 5290, 4362,
|
|
4325, 5584, 4515, 5008, 4789, 5196, 7660, 3829, 6034, 2925, 3762, 2545,
|
|
2437, 2553, 2167, 2761, 2015, 1969, 2350, 1731, 2367, 2078, 2026, 3518,
|
|
2214, 1908, 1780, 1640, 1976, 1647, 1921, 1716, 1895, 2150, 2419, 2451,
|
|
2147, 2327, 2251, 1644, 1750, 1481, 1570, 1524, 1562, 1668, 1386, 1494,
|
|
1637, 1880, 1431, 1472, 1637, 1363, 1762, 1597, 1999, 1564, 1341, 1388,
|
|
1530, 1476, 1617, 1488, 1368, 1906, 1403, 1695, 1535, 1598, 1318, 1234,
|
|
1358, 1406, 1698, 1554, 1591, 1758, 1426, 2389, 1946, 1533, 1308, 2701,
|
|
1525, 1653, 3580, 1889, 2913, 8164, 5154, 3762, 3356, 4360, 3589, 4844,
|
|
4232, 3781, 3882, 5924, 10790, 7185, 7442, 8214, 8509, 7484, 6939, 7391,
|
|
8210, 15572, 39680, 44810, 53873, 54639, 68227, 63428, 62386, 68504,
|
|
83073, 103858, 117573, 98089, 96793, 102337, 94714, 129568, 251584,
|
|
132026, 94579, 94516, 95722, 106495, 121824, 153983, 162338, 136608,
|
|
137104, 109872, 91114, 84757, 96339, 74251, 94314, 143216, 155837,
|
|
129968, 120201, 109913, 101588, 97332, 104611, 95310, 93419, 113345,
|
|
100743, 92152, 57565, 22533, 37564, 21823, 19980, 18277, 18402, 14344,
|
|
12142, 15842, 13677, 17631, 18294, 22270, 41422, 39296, 36688, 33512,
|
|
33831, 27582, 22276, 27516, 27317, 25505, 24426, 20566, 23045, 26766,
|
|
28185, 26169, 27011,
|
|
28642 // Blocks 1,990,000 to 1,999,999 in December 2019
|
|
};
|
|
const uint64_t block_range_size = 10000;
|
|
|
|
uint64_t num_block_sizes = sizeof(average_block_sizes) / sizeof(average_block_sizes[0]);
|
|
uint64_t weight = 0;
|
|
uint64_t table_index = start_block / block_range_size;
|
|
for (;;) {
|
|
if (num_blocks == 0)
|
|
{
|
|
break;
|
|
}
|
|
if (table_index >= num_block_sizes)
|
|
{
|
|
// Take all blocks beyond our table as having the size of the blocks
|
|
// in the last table entry i.e. in the most recent known block range
|
|
weight += num_blocks * average_block_sizes[num_block_sizes - 1];
|
|
break;
|
|
}
|
|
uint64_t portion_size = std::min(num_blocks, block_range_size - start_block % block_range_size);
|
|
weight += portion_size * average_block_sizes[table_index];
|
|
table_index++;
|
|
num_blocks -= portion_size;
|
|
start_block += portion_size;
|
|
}
|
|
return weight;
|
|
#endif
|
|
}
|
|
} // namespace tools
|