text: // 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 <string>
  #include <iomanip>
  #include <thread>
  
  #include <openssl/ssl.h>
  
  #include "unbound.h"
  
  #include "epee/string_tools.h"
  #include "epee/wipeable_string.h"
  #include "crypto/crypto.h"
  #include "util.h"
  #include "epee/misc_os_dependent.h"
  #include "epee/readline_buffer.h"
  #include "string_util.h"
  
  #include "i18n.h"
  
  #ifdef __GLIBC__
  #include <sys/resource.h>
  #include <gnu/libc-version.h>
  #endif
  
  #undef OXEN_DEFAULT_LOG_CATEGORY
  #define OXEN_DEFAULT_LOG_CATEGORY "util"
  
  namespace tools
  {
  
    static bool unbound_built_with_threads()
    {
      ub_ctx *ctx = ub_ctx_create();
      if (!ctx) return false; // cheat a bit, should not happen unless OOM
      char *oxen = strdup("oxen"), *unbound = strdup("unbound");
      ub_ctx_zone_add(ctx, oxen, unbound); // this calls ub_ctx_finalize first, then errors out with UB_SYNTAX
      free(unbound);
      free(oxen);
      // if no threads, bails out early with UB_NOERROR, otherwise fails with UB_AFTERFINAL id already finalized
      bool with_threads = ub_ctx_async(ctx, 1) != 0; // UB_AFTERFINAL is not defined in public headers, check any error
      ub_ctx_delete(ctx);
      MINFO("libunbound was built " << (with_threads ? "with" : "without") << " threads");
      return with_threads;
    }
  
    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))
      {
        MWARNING("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)
      {
        MERROR("Failed to determine the lockable memory limit");
        return -1;
      }
      return rlim.rlim_cur;
  #else
      return -1;
  #endif
    }
  
    bool on_startup()
    {
      mlog_configure("", true);
  
  #ifdef __GLIBC__
      const char *ver = ::gnu_get_libc_version();
      if (!strcmp(ver, "2.25"))
        MCLOG_RED(el::Level::Warning, "global", "Running with glibc " << ver << ", hangs may occur - change glibc version if possible");
  #endif
  
  #if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(LIBRESSL_VERSION_TEXT)
      SSL_library_init();
  #else
      OPENSSL_init_ssl(0, NULL);
  #endif
  
      if (!unbound_built_with_threads())
        MCLOG_RED(el::Level::Warning, "global", "libunbound was not built with threads enabled - crashes may occur");
  
      return true;
    }
    namespace
    {
      std::mutex max_concurrency_lock;
      unsigned max_concurrency = std::thread::hardware_concurrency();
    }
  
    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 {};
      }
    }
  
  #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(uint64_t ts)
    {
      char buffer[64];
      if (ts < 1234567890)
        return "<unknown>";
      time_t tt = ts;
      struct tm tm;
      epee::misc_utils::get_gmt_time(tt, tm);
      strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S UTC", &tm);
      return std::string(buffer);
    }
  
    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
    }
  }
  