LCOV - code coverage report
Current view: top level - common - monero_c_common.h (source / functions) Hit Total Coverage
Test: monero-c coverage @ 3fdc3a1 Lines: 86 89 96.6 %
Date: 2026-10-18 16:42:22 Functions: 304 307 99.0 %

          Line data    Source code
       1             : /**
       2             :  * Copyright (c) Libmonero
       3             :  *
       4             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  * of this software and associated documentation files (the "Software"), to deal
       6             :  * in the Software without restriction, including without limitation the rights
       7             :  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  * copies of the Software, and to permit persons to whom the Software is
       9             :  * furnished to do so, subject to the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included in all
      12             :  * copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      20             :  * SOFTWARE.
      21             :  */
      22             : 
      23             : #ifndef MONERO_C_COMMON_H
      24             : #define MONERO_C_COMMON_H
      25             : 
      26             : #include "utils/monero_c_utils.h"
      27             : #include "common/monero_c_rpc_connection.h"
      28             : 
      29             : #include "rapidjson/stringbuffer.h"
      30             : #include "rapidjson/writer.h"
      31             : 
      32             : #include <boost/optional.hpp>
      33             : #include <algorithm>
      34             : #include <cstdio>
      35             : #include <cstdlib>
      36             : #include <cstring>
      37             : #include <exception>
      38             : #include <memory>
      39             : #include <new>
      40             : #include <stdexcept>
      41             : #include <string>
      42             : #include <type_traits>
      43             : #include <vector>
      44             : 
      45             : namespace monero {
      46             : class monero_rpc_connection;
      47             : }
      48             : 
      49             : // helpers shared by the monero_* translation units. Not part of the public ABI
      50             : namespace monero_c {
      51             : 
      52             : // the monero-cpp connection behind a handle, shared with the daemons and wallets created from it
      53             : std::shared_ptr<monero::monero_rpc_connection> connection_of(::monero_rpc_connection* connection);
      54             : 
      55             : // makes the calling thread free, when it ends, what monero-project keeps for it. The first password hash of a thread, which is the one of a
      56             : // wallet that is made, opened or given a new password, allocates a scratchpad of 2 MB for the thread, and only slow_hash_free_state() frees
      57             : // it. monero-project calls that on its own threads, so a thread of the caller that has used a wallet and ends would keep the 2 MB until the
      58             : // process ends. guard() calls it, so every thread that calls the library is covered
      59             : void free_slow_hash_state_at_thread_exit() noexcept;
      60             : 
      61             : // records msg as the calling thread's last error. It never throws: if the message can't be
      62             : // stored because memory ran out, the last error is empty
      63             : void set_last_error(const char* msg) noexcept;
      64             : 
      65             : // records "<name> must not be null" as the last error, with the same guarantee
      66             : void set_null_error(const char* name) noexcept;
      67             : 
      68             : // returns the calling thread's last error, or "" if none
      69             : const std::string& last_error();
      70             : 
      71       30203 : inline std::string safe_str(const char* s) {
      72       30203 :   return s ? std::string(s) : std::string();
      73             : }
      74             : 
      75             : // the deepest nesting that a JSON argument may have. The parsers behind the arguments
      76             : // recurse once per level, so a deeper document overflows the stack
      77             : const size_t MAX_JSON_DEPTH = 64;
      78             : 
      79             : // throws std::invalid_argument if json nests objects and arrays deeper than MAX_JSON_DEPTH
      80             : void check_json_depth(const std::string& json);
      81             : 
      82             : // the longest mnemonic that is checked. A valid one is a few hundred bytes, and monero-project
      83             : // splits it in time quadratic in its length, so a megabyte of text would hold a call for minutes
      84             : const size_t MAX_MNEMONIC_LENGTH = 4096;
      85             : 
      86             : // throws std::invalid_argument if the mnemonic is longer than MAX_MNEMONIC_LENGTH
      87         692 : inline void check_mnemonic_length(const std::string& mnemonic) {
      88         692 :   if (mnemonic.size() > MAX_MNEMONIC_LENGTH) throw std::invalid_argument("mnemonic is longer than " + std::to_string(MAX_MNEMONIC_LENGTH) + " bytes");
      89         615 : }
      90             : 
      91             : // the longest period of a poll or a sync that is accepted, in milliseconds, 24.8 days. monero-cpp waits for a period with a signed long,
      92             : // which is 32 bits on Windows, so a period of 0 or one that doesn't fit makes the thread poll again at once, over and over, and with a
      93             : // remote daemon that is a flood of requests
      94             : const uint64_t MAX_PERIOD_MS = 2147483647;
      95             : 
      96             : // throws std::invalid_argument if the period is 0 or longer than MAX_PERIOD_MS
      97          41 : inline void check_period_ms(uint64_t period_ms) {
      98          41 :   if (period_ms == 0 || period_ms > MAX_PERIOD_MS) throw std::invalid_argument("period_ms must be between 1 and " + std::to_string(MAX_PERIOD_MS));
      99          24 : }
     100             : 
     101             : // mallocs at least 1 byte so a successful zero-length allocation is never
     102             : // mistaken for malloc() failure (which is the only case this throws)
     103        5122 : inline char* dup_string(const std::string& s) {
     104        5122 :   char* out = static_cast<char*>(std::malloc(s.size() + 1));
     105        5122 :   if (out == nullptr) throw std::bad_alloc();
     106        5122 :   std::copy(s.c_str(), s.c_str() + s.size() + 1, out);
     107        5122 :   return out;
     108             : }
     109             : 
     110             : // maps the C network enum to monero-cpp's, which has the same values except regtest. Regtest maps
     111             : // to mainnet, and the caller keeps the flag. Returns false if the value is out of range
     112             : template <class NetworkType>
     113        5284 : bool to_network_type(int32_t network_type, NetworkType& out) {
     114        5284 :   if (network_type < MONERO_UTILS_NETWORK_MAINNET || network_type > MONERO_UTILS_NETWORK_REGTEST) return false;
     115        4812 :   out = network_type == MONERO_UTILS_NETWORK_REGTEST ? NetworkType::MAINNET : static_cast<NetworkType>(network_type);
     116        4812 :   return true;
     117             : }
     118             : 
     119             : // like to_network_type(), but throws if the value is out of range
     120             : template <class NetworkType>
     121        3016 : NetworkType network_of(int32_t network_type) {
     122        3016 :   NetworkType out = NetworkType::MAINNET;
     123        3016 :   if (!to_network_type(network_type, out)) throw std::invalid_argument("unknown network type");
     124        2545 :   return out;
     125             : }
     126             : 
     127             : // runs fn, converting any thrown exception into MONERO_ERROR + last_error
     128             : template <class F>
     129       27596 : monero_result guard(F&& fn) {
     130       27596 :   free_slow_hash_state_at_thread_exit();
     131             :   try {
     132       27596 :     fn();
     133        9983 :     return MONERO_OK;
     134       17613 :   } catch (const std::exception& e) {
     135       17613 :     set_last_error(e.what());
     136       17613 :     return MONERO_ERROR;
     137           0 :   } catch (...) {
     138           0 :     set_last_error("unknown error");
     139           0 :     return MONERO_ERROR;
     140             :   }
     141             : }
     142             : 
     143             : // checks a required pointer. On failure, sets last_error to "<name> must not be null"
     144       55717 : inline bool require(const void* ptr, const char* name) {
     145       55717 :   if (ptr != nullptr) return true;
     146        2806 :   set_null_error(name);
     147        2806 :   return false;
     148             : }
     149             : 
     150             : // sets an output to NULL (a count to 0), unless the pointer to it is NULL. Done before the
     151             : // arguments are checked, so a failed call never leaves a stale pointer for the caller to free
     152             : template <class T>
     153       20864 : void reset_out(T* out) {
     154       20864 :   if (out != nullptr) *out = T();
     155       20864 : }
     156             : 
     157             : // copies a C array. Throws if it is NULL and the count is not zero. The name is the argument,
     158             : // for the error message
     159             : template <class T>
     160         140 : std::vector<T> array_of(const T* items, size_t count, const char* name) {
     161         140 :   if (count == 0) return std::vector<T>();
     162         111 :   if (items == nullptr) throw std::invalid_argument(std::string(name) + " must not be null");
     163          83 :   return std::vector<T>(items, items + count);
     164             : }
     165             : 
     166             : // copies a C array of strings. Throws if the array is NULL and the count is not zero, or if
     167             : // an element is NULL
     168        1080 : inline std::vector<std::string> string_array(const char* const* items, size_t count, const char* name) {
     169        1080 :   std::vector<std::string> out;
     170        1080 :   if (count == 0) return out;
     171        1037 :   if (items == nullptr) throw std::invalid_argument(std::string(name) + " must not be null");
     172        2467 :   for (size_t i = 0; i < count; i++) {
     173        1516 :     if (items[i] == nullptr) throw std::invalid_argument(std::string(name) + " must not contain NULL");
     174        1474 :     out.push_back(items[i]);
     175             :   }
     176         951 :   return out;
     177          86 : }
     178             : 
     179             : // maps an optional argument (NULL means none) to boost::optional
     180             : template <class T>
     181         400 : boost::optional<T> optional_of(const T* value) {
     182         400 :   boost::optional<T> out;
     183         400 :   if (value != nullptr) out = *value;
     184         400 :   return out;
     185             : }
     186             : 
     187             : // copies binary data into a new buffer that monero_utils_free() releases. The buffer is
     188             : // never NULL, so an empty result is still a valid pointer
     189        1503 : inline uint8_t* dup_buffer(const std::string& bin, size_t* out_len) {
     190        1503 :   uint8_t* out = static_cast<uint8_t*>(std::malloc(bin.empty() ? 1 : bin.size()));
     191        1503 :   if (out == nullptr) throw std::bad_alloc();
     192        1503 :   std::copy(bin.begin(), bin.end(), out);
     193        1503 :   *out_len = bin.size();
     194        1503 :   return out;
     195             : }
     196             : 
     197             : // copies items into a new array that free() releases with monero_utils_free(). The
     198             : // allocation is never NULL, so an empty result is still a valid pointer
     199             : template <class T>
     200         256 : T* dup_array(const std::vector<T>& items, size_t* out_count) {
     201             :   static_assert(std::is_trivially_copyable<T>::value, "dup_array assigns into malloc'd memory, so T must be trivially copyable");
     202         256 :   T* out = static_cast<T*>(std::malloc((items.empty() ? 1 : items.size()) * sizeof(T)));
     203         256 :   if (out == nullptr) throw std::bad_alloc();
     204        1059 :   for (size_t i = 0; i < items.size(); i++) out[i] = items[i];
     205         256 :   *out_count = items.size();
     206         256 :   return out;
     207             : }
     208             : 
     209             : // serializes a list of monero-cpp objects as a JSON array. Null items stay null
     210             : template <class T>
     211         148 : std::string json_of_list(const std::vector<std::shared_ptr<T>>& items) {
     212         148 :   std::string json = "[";
     213         736 :   for (size_t i = 0; i < items.size(); i++) {
     214         588 :     if (i > 0) json += ",";
     215         588 :     json += items[i] ? items[i]->serialize() : std::string("null");
     216             :   }
     217         296 :   return json + "]";
     218         148 : }
     219             : 
     220             : // serializes a list of monero-cpp objects that are held by value
     221             : template <class T>
     222         192 : std::string json_of_list(const std::vector<T>& items) {
     223         192 :   std::string json = "[";
     224         392 :   for (size_t i = 0; i < items.size(); i++) {
     225         200 :     if (i > 0) json += ",";
     226         200 :     json += items[i].serialize();
     227             :   }
     228         384 :   return json + "]";
     229         192 : }
     230             : 
     231             : // serializes a JSON array of strings, e.g. hashes or hexes
     232          37 : inline std::string json_of_strings(const std::vector<std::string>& items) {
     233          37 :   rapidjson::StringBuffer buffer;
     234          37 :   rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
     235          37 :   writer.StartArray();
     236         109 :   for (const auto& item : items) writer.String(item.c_str(), static_cast<rapidjson::SizeType>(item.size()));
     237          37 :   writer.EndArray();
     238          74 :   return buffer.GetString();
     239          37 : }
     240             : 
     241             : } // namespace monero_c
     242             : 
     243             : #endif // MONERO_C_COMMON_H

Generated by: LCOV version 1.16