LCOV - code coverage report
Current view: top level - daemon - monero_c_daemon.cpp (source / functions) Hit Total Coverage
Test: monero-c coverage @ 3fdc3a1 Lines: 518 527 98.3 %
Date: 2026-10-18 16:42:22 Functions: 223 227 98.2 %

          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             : #include "daemon/monero_c_daemon.h"
      24             : #include "common/monero_c_common.h"
      25             : #include "daemon/monero_daemon_model.h"
      26             : #include "daemon/monero_daemon_rpc.h"
      27             : #include "utils/gen_utils.h"
      28             : 
      29             : #include "misc_language.h"
      30             : #include "rapidjson/document.h"
      31             : #include "rapidjson/stringbuffer.h"
      32             : #include "rapidjson/writer.h"
      33             : 
      34             : #include <algorithm>
      35             : #include <boost/optional.hpp>
      36             : #include <cstdio>
      37             : #include <map>
      38             : #include <memory>
      39             : #include <mutex>
      40             : #include <stdexcept>
      41             : #include <string>
      42             : #include <vector>
      43             : 
      44             : struct monero_daemon_listener;
      45             : 
      46             : namespace monero_c {
      47             : 
      48             : // the handles of the listeners that are added to one daemon. get_listeners() reads this list, and not the set of monero-cpp, which holds the
      49             : // adapters inside the handles: another thread can free a listener after the copy of that set, and the read of the adapter that follows would
      50             : // be a read of freed memory. The handles are only compared and returned here, never read
      51             : struct daemon_listener_registry {
      52             :   std::mutex mutex;
      53             :   std::vector<monero_daemon_listener*> handles;
      54             : 
      55         253 :   void add(monero_daemon_listener* handle) {
      56         253 :     std::lock_guard<std::mutex> lock(mutex);
      57         253 :     if (std::find(handles.begin(), handles.end(), handle) == handles.end()) handles.push_back(handle);
      58         253 :   }
      59             : 
      60         250 :   void remove(monero_daemon_listener* handle) {
      61         250 :     std::lock_guard<std::mutex> lock(mutex);
      62         250 :     handles.erase(std::remove(handles.begin(), handles.end(), handle), handles.end());
      63         250 :   }
      64             : 
      65           7 :   void clear() {
      66           7 :     std::lock_guard<std::mutex> lock(mutex);
      67           7 :     handles.clear();
      68           7 :   }
      69             : 
      70         255 :   std::vector<monero_daemon_listener*> copy() {
      71         255 :     std::lock_guard<std::mutex> lock(mutex);
      72         510 :     return handles;
      73         255 :   }
      74             : };
      75             : 
      76             : } // namespace monero_c
      77             : 
      78             : // state behind a monero_daemon handle
      79             : struct monero_daemon {
      80             :   std::shared_ptr<monero::monero_daemon_rpc> rpc;
      81             :   std::shared_ptr<monero_c::daemon_listener_registry> registry = std::make_shared<monero_c::daemon_listener_registry>();
      82             : };
      83             : 
      84             : namespace {
      85             : 
      86             : using monero_c::array_of;
      87             : using monero_c::check_json_depth;
      88             : using monero_c::check_period_ms;
      89             : using monero_c::dup_array;
      90             : using monero_c::dup_string;
      91             : using monero_c::guard;
      92             : using monero_c::json_of_list;
      93             : using monero_c::json_of_strings;
      94             : using monero_c::optional_of;
      95             : using monero_c::require;
      96             : using monero_c::reset_out;
      97             : using monero_c::safe_str;
      98             : using monero_c::set_last_error;
      99             : using monero_c::string_array;
     100             : 
     101             : // monero-cpp lists the height of every block in the range before the first request, so a range
     102             : // with no real end, such as UINT64_MAX, never finishes. The chunked variant has no such limit
     103             : const uint64_t MAX_BLOCKS_BY_RANGE = 100000;
     104             : 
     105             : // splits a JSON array into its elements, each serialized again as a JSON document
     106         474 : std::vector<std::string> split_json_array(const std::string& json) {
     107         474 :   check_json_depth(json);
     108         465 :   rapidjson::Document doc;
     109         465 :   doc.Parse(json.c_str());
     110         465 :   if (doc.HasParseError() || !doc.IsArray()) throw std::invalid_argument("expected a JSON array");
     111             : 
     112         174 :   std::vector<std::string> items;
     113         376 :   for (const auto& value : doc.GetArray()) {
     114         202 :     rapidjson::StringBuffer buffer;
     115         202 :     rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
     116         202 :     value.Accept(writer);
     117         202 :     items.push_back(buffer.GetString());
     118         376 :   }
     119         348 :   return items;
     120         465 : }
     121             : 
     122             : // deserializes each element of a JSON array into a monero-cpp object
     123             : template <class T>
     124         474 : std::vector<std::shared_ptr<T>> parse_list(const std::string& json) {
     125         474 :   std::vector<std::shared_ptr<T>> items;
     126         669 :   for (const std::string& item : split_json_array(json)) {
     127         195 :     items.push_back(gen_utils::deserialize<T>(item));
     128             :   }
     129         160 :   return items;
     130         314 : }
     131             : 
     132             : // monero-cpp's monero_tx_backlog_entry has no fields yet, so each entry is an empty object
     133           0 : std::string json_of_backlog(const std::vector<monero::monero_tx_backlog_entry>& entries) {
     134           0 :   std::string json = "[";
     135           0 :   for (size_t i = 0; i < entries.size(); i++) {
     136           0 :     if (i > 0) json += ",";
     137           0 :     json += "{}";
     138             :   }
     139           0 :   return json + "]";
     140           0 : }
     141             : 
     142             : // serializes one result object. A null result (the daemon has no such item) becomes NULL
     143             : template <class T>
     144          31 : char* dup_json(const std::shared_ptr<T>& item) {
     145          31 :   return item ? dup_string(item->serialize()) : nullptr;
     146             : }
     147             : 
     148             : // serializes one result object held by value
     149             : template <class T>
     150           1 : char* dup_json_value(const T& item) {
     151           1 :   return dup_string(item.serialize());
     152             : }
     153             : 
     154             : // the models of monero-cpp point to their parents with shared_ptr (a tx to its block, an input or an output to its tx), so a block
     155             : // or a tx that nobody releases is never freed. release_models() resets those pointers once the result is serialized. monero_utils::free()
     156             : // does it for the wallet models, and it leaves the miner tx of a block and the inputs and outputs of a plain tx
     157          26 : void release_models(const std::shared_ptr<monero::monero_tx>& tx) {
     158          26 :   if (tx == nullptr) return;
     159          26 :   tx->m_block.reset();
     160          46 :   for (const std::shared_ptr<monero::monero_output>& input : tx->m_inputs) {
     161          20 :     if (input != nullptr) input->m_tx.reset();
     162             :   }
     163          62 :   for (const std::shared_ptr<monero::monero_output>& output : tx->m_outputs) {
     164          36 :     if (output != nullptr) output->m_tx.reset();
     165             :   }
     166             : }
     167             : 
     168          16 : void release_models(const std::shared_ptr<monero::monero_block>& block) {
     169          16 :   if (block == nullptr) return;
     170          16 :   release_models(block->m_miner_tx);
     171          16 :   for (const std::shared_ptr<monero::monero_tx>& tx : block->m_txs) release_models(tx);
     172             : }
     173             : 
     174             : template <class T>
     175          15 : void release_models(const std::vector<std::shared_ptr<T>>& items) {
     176          37 :   for (const std::shared_ptr<T>& item : items) release_models(item);
     177          15 : }
     178             : 
     179             : // serializes one block or tx and then releases it, with a scope handler so that it also runs when the call throws
     180             : template <class T>
     181           4 : char* dup_json_released(const std::shared_ptr<T>& item) {
     182           8 :   auto release_item = epee::misc_utils::create_scope_leave_handler([&] { release_models(item); });
     183           8 :   return dup_json(item);
     184           4 : }
     185             : 
     186             : // serializes a list of blocks or txs and then releases them
     187             : template <class T>
     188           3 : char* dup_json_list_released(const std::vector<std::shared_ptr<T>>& items) {
     189           6 :   auto release_items = epee::misc_utils::create_scope_leave_handler([&] { release_models(items); });
     190           6 :   return dup_string(json_of_list(items));
     191           3 : }
     192             : 
     193             : // the blocks that hold txs of the daemon, so that each tx has its height and its timestamp. monero-cpp reads them into a block that each tx
     194             : // has for itself, so the txs of one block are put in one block, in the order that the blocks first appear, and the txs that are in the
     195             : // pool, which have no height, in one block that has none
     196          11 : std::vector<std::shared_ptr<monero::monero_block>> blocks_of_txs(const std::vector<std::shared_ptr<monero::monero_tx>>& txs) {
     197          11 :   std::vector<std::shared_ptr<monero::monero_block>> blocks;
     198          11 :   std::map<uint64_t, std::shared_ptr<monero::monero_block>> confirmed;
     199          11 :   std::shared_ptr<monero::monero_block> pool;
     200          21 :   for (const std::shared_ptr<monero::monero_tx>& tx : txs) {
     201          10 :     if (tx == nullptr) continue;
     202          10 :     if (tx->m_block != nullptr && tx->m_block->m_height != boost::none) {
     203           7 :       std::shared_ptr<monero::monero_block>& block = confirmed[*tx->m_block->m_height];
     204           7 :       if (block == nullptr) {
     205           6 :         block = std::make_shared<monero::monero_block>();
     206           6 :         block->m_height = tx->m_block->m_height;
     207           6 :         block->m_timestamp = tx->m_block->m_timestamp;
     208           6 :         blocks.push_back(block);
     209             :       }
     210           7 :       block->m_txs.push_back(tx);
     211             :     } else {
     212           3 :       if (pool == nullptr) {
     213           3 :         pool = std::make_shared<monero::monero_block>();
     214           3 :         blocks.push_back(pool);
     215             :       }
     216           3 :       pool->m_txs.push_back(tx);
     217             :     }
     218             :   }
     219          22 :   return blocks;
     220          11 : }
     221             : 
     222             : // serializes txs as the blocks that hold them, {"blocks":[...]}, and releases them. The new blocks point to no parent, and they go when the
     223             : // call returns
     224          11 : char* dup_json_blocks_released(const std::vector<std::shared_ptr<monero::monero_tx>>& txs) {
     225          22 :   auto release_txs = epee::misc_utils::create_scope_leave_handler([&] { release_models(txs); });
     226          22 :   return dup_string("{\"blocks\":" + json_of_list(blocks_of_txs(txs)) + "}");
     227          11 : }
     228             : 
     229             : // converts monero-cpp's key image status to the C enum, which has the same values
     230           2 : monero_key_image_spent_status to_c_status(monero::monero_key_image_spent_status status) {
     231           2 :   return static_cast<monero_key_image_spent_status>(static_cast<int>(status));
     232             : }
     233             : 
     234             : // forwards the daemon's block header notifications to the C callbacks of a listener
     235             : class listener_adapter : public monero::monero_daemon_listener {
     236             : public:
     237             :   monero_daemon_listener_callbacks callbacks = {};
     238             : 
     239           1 :   void on_block_header(const std::shared_ptr<monero::monero_block_header>& header) override {
     240           1 :     monero::monero_daemon_listener::on_block_header(header);
     241           1 :     if (callbacks.on_block_header == nullptr || !header) return;
     242           1 :     std::string json = header->serialize();
     243           1 :     callbacks.on_block_header(callbacks.user_data, json.c_str());
     244           1 :   }
     245             : };
     246             : 
     247             : } // namespace
     248             : 
     249             : struct monero_daemon_listener {
     250             :   listener_adapter adapter;
     251             :   std::weak_ptr<monero::monero_daemon_rpc> registered_with;
     252             :   std::weak_ptr<monero_c::daemon_listener_registry> registry;  // the list of the daemon that it is added to, set with registered_with
     253             :   std::mutex registration_mutex; // guards registered_with and registry, so the check and the registration in add_listener are one step
     254             : };
     255             : 
     256             : extern "C" {
     257             : 
     258             : // ------------------------------ CONNECTION ----------------------------------
     259             : 
     260          19 : monero_result monero_daemon_connect(const char* uri, const char* username, const char* password, const char* proxy_uri, uint32_t timeout_ms, monero_daemon** out_daemon) {
     261          19 :   reset_out(out_daemon);
     262          19 :   if (!require(out_daemon, "out_daemon")) return MONERO_ERROR;
     263          16 :   std::string uri_str = safe_str(uri);
     264          16 :   if (uri_str.empty()) { set_last_error("uri must not be empty"); return MONERO_ERROR; }
     265          26 :   return guard([&] {
     266          13 :     boost::optional<uint32_t> timeout;
     267          13 :     if (timeout_ms != 0) timeout = timeout_ms;
     268          13 :     std::unique_ptr<monero_daemon> daemon(new monero_daemon());
     269          21 :     daemon->rpc = std::make_shared<monero::monero_daemon_rpc>(uri_str, safe_str(username), safe_str(password), safe_str(proxy_uri), std::string(), timeout);
     270          11 :     *out_daemon = daemon.release();
     271          26 :   });
     272          16 : }
     273             : 
     274          15 : monero_result monero_daemon_connect_with(::monero_rpc_connection* connection, monero_daemon** out_daemon) {
     275          15 :   reset_out(out_daemon);
     276          15 :   if (!require(connection, "connection") || !require(out_daemon, "out_daemon")) return MONERO_ERROR;
     277          20 :   return guard([&] {
     278          10 :     std::unique_ptr<monero_daemon> daemon(new monero_daemon());
     279          10 :     daemon->rpc = std::make_shared<monero::monero_daemon_rpc>(monero_c::connection_of(connection));
     280          10 :     *out_daemon = daemon.release();
     281          20 :   });
     282             : }
     283             : 
     284          24 : void monero_daemon_free(monero_daemon* daemon) {
     285          24 :   delete daemon;
     286          24 : }
     287             : 
     288           9 : monero_result monero_daemon_get_rpc_connection(monero_daemon* daemon, char** out_json) {
     289           9 :   reset_out(out_json);
     290           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     291          10 :   return guard([&] { *out_json = dup_string(daemon->rpc->get_rpc_connection()->serialize()); });
     292             : }
     293             : 
     294          13 : monero_result monero_daemon_set_poll_period(monero_daemon* daemon, uint64_t period_ms) {
     295          13 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     296          20 :   return guard([&] {
     297          10 :     check_period_ms(period_ms);
     298           6 :     daemon->rpc->set_poll_period_in_ms(period_ms);
     299          10 :   });
     300             : }
     301             : 
     302             : // -------------------------------- LISTENERS ---------------------------------
     303             : 
     304         256 : monero_result monero_daemon_listener_create(const monero_daemon_listener_callbacks* callbacks, monero_daemon_listener** out_listener) {
     305         256 :   reset_out(out_listener);
     306         256 :   if (!require(callbacks, "callbacks") || !require(out_listener, "out_listener")) return MONERO_ERROR;
     307         500 :   return guard([&] {
     308         250 :     std::unique_ptr<monero_daemon_listener> listener(new monero_daemon_listener());
     309         250 :     listener->adapter.callbacks = *callbacks;
     310         250 :     *out_listener = listener.release();
     311         500 :   });
     312             : }
     313             : 
     314         253 : void monero_daemon_listener_free(monero_daemon_listener* listener) {
     315         253 :   if (listener == nullptr) return;
     316         250 :   std::shared_ptr<monero::monero_daemon_rpc> rpc;
     317         250 :   std::shared_ptr<monero_c::daemon_listener_registry> registry;
     318             :   {
     319         250 :     std::lock_guard<std::mutex> lock(listener->registration_mutex);
     320         250 :     rpc = listener->registered_with.lock();
     321         250 :     registry = listener->registry.lock();
     322         250 :   }
     323         250 :   if (rpc) {
     324             :     try {
     325         125 :       rpc->remove_listener(listener->adapter);
     326           0 :     } catch (...) {
     327             :       // release the listener either way, since the caller can't act on a failed removal
     328           0 :     }
     329             :   }
     330         250 :   if (registry) registry->remove(listener);
     331         250 :   delete listener;
     332         250 : }
     333             : 
     334         261 : monero_result monero_daemon_add_listener(monero_daemon* daemon, monero_daemon_listener* listener) {
     335         261 :   if (!require(daemon, "daemon") || !require(listener, "listener")) return MONERO_ERROR;
     336         510 :   return guard([&] {
     337             :     // free unregisters from one daemon only, so a listener can't be registered with two
     338         255 :     std::lock_guard<std::mutex> lock(listener->registration_mutex);
     339         255 :     std::shared_ptr<monero::monero_daemon_rpc> current = listener->registered_with.lock();
     340             :     // remove_listeners() of that daemon leaves the link of the listener as it was, so the set of the daemon says if it still holds it
     341         255 :     if (current && current != daemon->rpc && current->get_listeners().count(&listener->adapter) != 0) throw std::runtime_error("listener is already registered with another daemon");
     342         253 :     daemon->rpc->add_listener(listener->adapter);
     343         253 :     listener->registered_with = daemon->rpc;
     344         253 :     listener->registry = daemon->registry;
     345         253 :     daemon->registry->add(listener);
     346         512 :   });
     347             : }
     348             : 
     349         129 : monero_result monero_daemon_remove_listener(monero_daemon* daemon, monero_daemon_listener* listener) {
     350         129 :   if (!require(daemon, "daemon") || !require(listener, "listener")) return MONERO_ERROR;
     351         250 :   return guard([&] {
     352             :     // remove_listener waits for callbacks, so the lock is taken after it returns
     353         125 :     daemon->rpc->remove_listener(listener->adapter);
     354         125 :     daemon->registry->remove(listener);
     355         125 :     std::lock_guard<std::mutex> lock(listener->registration_mutex);
     356         125 :     if (listener->registered_with.lock() == daemon->rpc) {
     357         125 :       listener->registered_with.reset();
     358         125 :       listener->registry.reset();
     359             :     }
     360         250 :   });
     361             : }
     362             : 
     363         263 : monero_result monero_daemon_get_listeners(monero_daemon* daemon, monero_daemon_listener*** out_listeners, size_t* out_count) {
     364         263 :   reset_out(out_listeners);
     365         263 :   reset_out(out_count);
     366         263 :   if (!require(daemon, "daemon") || !require(out_listeners, "out_listeners") || !require(out_count, "out_count")) return MONERO_ERROR;
     367         510 :   return guard([&] { *out_listeners = dup_array(daemon->registry->copy(), out_count); });
     368             : }
     369             : 
     370          10 : monero_result monero_daemon_remove_listeners(monero_daemon* daemon) {
     371          10 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     372          14 :   return guard([&] {
     373           7 :     daemon->rpc->remove_listeners();
     374           7 :     daemon->registry->clear();
     375           7 :   });
     376             : }
     377             : 
     378             : // ---------------------------------- GENERAL ---------------------------------
     379             : 
     380           9 : monero_result monero_daemon_get_version(monero_daemon* daemon, char** out_json) {
     381           9 :   reset_out(out_json);
     382           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     383           8 :   return guard([&] { *out_json = dup_json_value(daemon->rpc->get_version()); });
     384             : }
     385             : 
     386           9 : monero_result monero_daemon_is_trusted(monero_daemon* daemon, bool* out_trusted) {
     387           9 :   if (!require(daemon, "daemon") || !require(out_trusted, "out_trusted")) return MONERO_ERROR;
     388           8 :   return guard([&] { *out_trusted = daemon->rpc->is_trusted(); });
     389             : }
     390             : 
     391         258 : monero_result monero_daemon_get_height(monero_daemon* daemon, uint64_t* out_height) {
     392         258 :   if (!require(daemon, "daemon") || !require(out_height, "out_height")) return MONERO_ERROR;
     393         502 :   return guard([&] { *out_height = daemon->rpc->get_height(); });
     394             : }
     395             : 
     396          12 : monero_result monero_daemon_get_block_hash(monero_daemon* daemon, uint64_t height, char** out_hash) {
     397          12 :   reset_out(out_hash);
     398          12 :   if (!require(daemon, "daemon") || !require(out_hash, "out_hash")) return MONERO_ERROR;
     399          12 :   return guard([&] { *out_hash = dup_string(daemon->rpc->get_block_hash(height)); });
     400             : }
     401             : 
     402          12 : monero_result monero_daemon_get_info(monero_daemon* daemon, char** out_json) {
     403          12 :   reset_out(out_json);
     404          12 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     405          12 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_info()); });
     406             : }
     407             : 
     408           9 : monero_result monero_daemon_get_sync_info(monero_daemon* daemon, char** out_json) {
     409           9 :   reset_out(out_json);
     410           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     411           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_sync_info()); });
     412             : }
     413             : 
     414           9 : monero_result monero_daemon_get_network_stats(monero_daemon* daemon, char** out_json) {
     415           9 :   reset_out(out_json);
     416           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     417           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_network_stats()); });
     418             : }
     419             : 
     420           9 : monero_result monero_daemon_get_hard_fork_info(monero_daemon* daemon, char** out_json) {
     421           9 :   reset_out(out_json);
     422           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     423           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_hard_fork_info()); });
     424             : }
     425             : 
     426           9 : monero_result monero_daemon_get_alt_chains(monero_daemon* daemon, char** out_json) {
     427           9 :   reset_out(out_json);
     428           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     429           8 :   return guard([&] { *out_json = dup_string(json_of_list(daemon->rpc->get_alt_chains())); });
     430             : }
     431             : 
     432           9 : monero_result monero_daemon_get_alt_block_hashes(monero_daemon* daemon, char** out_json) {
     433           9 :   reset_out(out_json);
     434           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     435           8 :   return guard([&] { *out_json = dup_string(json_of_strings(daemon->rpc->get_alt_block_hashes())); });
     436             : }
     437             : 
     438             : // ---------------------------------- MINING ----------------------------------
     439             : 
     440          11 : monero_result monero_daemon_get_block_template(monero_daemon* daemon, const char* wallet_address, const int32_t* reserve_size, char** out_json) {
     441          11 :   reset_out(out_json);
     442          11 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     443          17 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_block_template(safe_str(wallet_address), optional_of(reserve_size))); });
     444             : }
     445             : 
     446           9 : monero_result monero_daemon_get_miner_data(monero_daemon* daemon, char** out_json) {
     447           9 :   reset_out(out_json);
     448           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     449           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_miner_data()); });
     450             : }
     451             : 
     452          11 : monero_result monero_daemon_calculate_pow(monero_daemon* daemon, uint32_t major_version, uint64_t height, const char* block_blob, const char* seed_hash, char** out_pow_hash) {
     453          11 :   reset_out(out_pow_hash);
     454          11 :   if (!require(daemon, "daemon") || !require(out_pow_hash, "out_pow_hash")) return MONERO_ERROR;
     455          22 :   return guard([&] { *out_pow_hash = dup_string(daemon->rpc->calculate_pow(major_version, height, safe_str(block_blob), safe_str(seed_hash))); });
     456             : }
     457             : 
     458         162 : monero_result monero_daemon_add_auxiliary_pow(monero_daemon* daemon, const char* block_template_blob, const char* aux_pow_json, char** out_json) {
     459         162 :   reset_out(out_json);
     460         162 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     461         314 :   return guard([&] {
     462         440 :     *out_json = dup_json(daemon->rpc->add_auxiliary_pow(safe_str(block_template_blob), parse_list<monero::monero_auxiliary_pow>(safe_str(aux_pow_json))));
     463         157 :   });
     464             : }
     465             : 
     466           9 : monero_result monero_daemon_get_mining_status(monero_daemon* daemon, char** out_json) {
     467           9 :   reset_out(out_json);
     468           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     469           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_mining_status()); });
     470             : }
     471             : 
     472          11 : monero_result monero_daemon_start_mining(monero_daemon* daemon, const char* address, const uint64_t* num_threads, const bool* is_background, const bool* ignore_battery) {
     473          11 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     474          16 :   return guard([&] { daemon->rpc->start_mining(safe_str(address), optional_of(num_threads), optional_of(is_background), optional_of(ignore_battery)); });
     475             : }
     476             : 
     477           7 : monero_result monero_daemon_stop_mining(monero_daemon* daemon) {
     478           7 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     479           8 :   return guard([&] { daemon->rpc->stop_mining(); });
     480             : }
     481             : 
     482          17 : monero_result monero_daemon_generate_blocks(monero_daemon* daemon, const char* wallet_address, uint64_t num_blocks, const char* prev_block_hash, const uint32_t* starting_nonce, char** out_json) {
     483          17 :   reset_out(out_json);
     484          17 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     485          24 :   return guard([&] {
     486          12 :     boost::optional<std::string> prev_hash;
     487          12 :     if (prev_block_hash != nullptr) prev_hash = std::string(prev_block_hash);
     488          18 :     *out_json = dup_json(daemon->rpc->generate_blocks(safe_str(wallet_address), num_blocks, prev_hash, optional_of(starting_nonce)));
     489          24 :   });
     490             : }
     491             : 
     492           8 : monero_result monero_daemon_submit_block(monero_daemon* daemon, const char* block_blob) {
     493           8 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     494          10 :   return guard([&] { daemon->rpc->submit_block(safe_str(block_blob)); });
     495             : }
     496             : 
     497          11 : monero_result monero_daemon_submit_blocks(monero_daemon* daemon, const char* const* block_blobs, size_t num_block_blobs) {
     498          11 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     499          16 :   return guard([&] { daemon->rpc->submit_blocks(string_array(block_blobs, num_block_blobs, "block_blobs")); });
     500             : }
     501             : 
     502             : // ----------------------------------- BLOCKS ---------------------------------
     503             : 
     504           9 : monero_result monero_daemon_get_last_block_header(monero_daemon* daemon, char** out_json) {
     505           9 :   reset_out(out_json);
     506           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     507           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_last_block_header()); });
     508             : }
     509             : 
     510         160 : monero_result monero_daemon_get_block_header_by_hash(monero_daemon* daemon, const char* block_hash, char** out_json) {
     511         160 :   reset_out(out_json);
     512         160 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     513         464 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_block_header_by_hash(safe_str(block_hash))); });
     514             : }
     515             : 
     516           9 : monero_result monero_daemon_get_block_header_by_height(monero_daemon* daemon, uint64_t height, char** out_json) {
     517           9 :   reset_out(out_json);
     518           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     519           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_block_header_by_height(height)); });
     520             : }
     521             : 
     522           9 : monero_result monero_daemon_get_block_headers_by_range(monero_daemon* daemon, uint64_t start_height, uint64_t end_height, char** out_json) {
     523           9 :   reset_out(out_json);
     524           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     525           8 :   return guard([&] { *out_json = dup_string(json_of_list(daemon->rpc->get_block_headers_by_range(start_height, end_height))); });
     526             : }
     527             : 
     528          11 : monero_result monero_daemon_get_block_by_hash(monero_daemon* daemon, const char* block_hash, char** out_json) {
     529          11 :   reset_out(out_json);
     530          11 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     531          16 :   return guard([&] { *out_json = dup_json_released(daemon->rpc->get_block_by_hash(safe_str(block_hash))); });
     532             : }
     533             : 
     534          13 : monero_result monero_daemon_get_blocks_by_hash(monero_daemon* daemon, const char* const* block_hashes, size_t num_block_hashes, uint64_t start_height, bool prune, uint64_t max_block_count, char** out_json) {
     535          13 :   reset_out(out_json);
     536          13 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     537          16 :   return guard([&] {
     538          13 :     auto result = daemon->rpc->get_blocks_by_hash(string_array(block_hashes, num_block_hashes, "block_hashes"), start_height, prune, max_block_count);
     539           2 :     auto release_result = epee::misc_utils::create_scope_leave_handler([&] { if (result != nullptr) release_models(result->m_blocks); });
     540           1 :     *out_json = dup_json(result);
     541           9 :   });
     542             : }
     543             : 
     544          10 : monero_result monero_daemon_get_block_by_height(monero_daemon* daemon, uint64_t height, char** out_json) {
     545          10 :   reset_out(out_json);
     546          10 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     547          10 :   return guard([&] { *out_json = dup_json_released(daemon->rpc->get_block_by_height(height)); });
     548             : }
     549             : 
     550          12 : monero_result monero_daemon_get_blocks_by_height(monero_daemon* daemon, const uint64_t* heights, size_t num_heights, char** out_json) {
     551          12 :   reset_out(out_json);
     552          12 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     553          19 :   return guard([&] { *out_json = dup_json_list_released(daemon->rpc->get_blocks_by_height(array_of(heights, num_heights, "heights"))); });
     554             : }
     555             : 
     556          19 : monero_result monero_daemon_get_blocks_by_range(monero_daemon* daemon, const uint64_t* start_height, const uint64_t* end_height, char** out_json) {
     557          19 :   reset_out(out_json);
     558          19 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     559          28 :   return guard([&] {
     560          14 :     uint64_t start = start_height != nullptr ? *start_height : 0;
     561          14 :     if (end_height != nullptr && *end_height >= start && *end_height - start >= MAX_BLOCKS_BY_RANGE) {
     562           4 :       throw std::invalid_argument("range is longer than " + std::to_string(MAX_BLOCKS_BY_RANGE) + " blocks, use monero_daemon_get_blocks_by_range_chunked()");
     563             :     }
     564          10 :     *out_json = dup_json_list_released(daemon->rpc->get_blocks_by_range(optional_of(start_height), optional_of(end_height)));
     565          15 :   });
     566             : }
     567             : 
     568          12 : monero_result monero_daemon_get_blocks_by_range_chunked(monero_daemon* daemon, const uint64_t* start_height, const uint64_t* end_height, const uint64_t* max_chunk_size, char** out_json) {
     569          12 :   reset_out(out_json);
     570          12 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     571          14 :   return guard([&] {
     572           7 :     *out_json = dup_json_list_released(daemon->rpc->get_blocks_by_range_chunked(optional_of(start_height), optional_of(end_height), optional_of(max_chunk_size)));
     573           8 :   });
     574             : }
     575             : 
     576          13 : monero_result monero_daemon_get_block_hashes(monero_daemon* daemon, const char* const* block_hashes, size_t num_block_hashes, char** out_json) {
     577          13 :   reset_out(out_json);
     578          13 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     579          20 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_block_hashes(string_array(block_hashes, num_block_hashes, "block_hashes"))); });
     580             : }
     581             : 
     582           6 : monero_result monero_daemon_wait_for_next_block_header(monero_daemon* daemon, char** out_json) {
     583           6 :   reset_out(out_json);
     584           6 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     585           2 :   return guard([&] { *out_json = dup_json(daemon->rpc->wait_for_next_block_header()); });
     586             : }
     587             : 
     588             : // ---------------------------- TRANSACTIONS ----------------------------------
     589             : 
     590          14 : monero_result monero_daemon_get_tx(monero_daemon* daemon, const char* tx_hash, bool prune, char** out_json) {
     591          14 :   reset_out(out_json);
     592          14 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     593          18 :   return guard([&] {
     594          13 :     std::shared_ptr<monero::monero_tx> tx = daemon->rpc->get_tx(safe_str(tx_hash), prune);
     595          11 :     if (tx != nullptr) *out_json = dup_json_blocks_released({tx});
     596          17 :   });
     597             : }
     598             : 
     599          16 : monero_result monero_daemon_get_txs(monero_daemon* daemon, const char* const* tx_hashes, size_t num_tx_hashes, bool prune, char** out_json) {
     600          16 :   reset_out(out_json);
     601          16 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     602          27 :   return guard([&] { *out_json = dup_json_blocks_released(daemon->rpc->get_txs(string_array(tx_hashes, num_tx_hashes, "tx_hashes"), prune)); });
     603             : }
     604             : 
     605         160 : monero_result monero_daemon_get_tx_hex(monero_daemon* daemon, const char* tx_hash, bool prune, char** out_tx_hex) {
     606         160 :   reset_out(out_tx_hex);
     607         160 :   if (!require(daemon, "daemon") || !require(out_tx_hex, "out_tx_hex")) return MONERO_ERROR;
     608         310 :   return guard([&] {
     609         309 :     boost::optional<std::string> hex = daemon->rpc->get_tx_hex(safe_str(tx_hash), prune);
     610           1 :     *out_tx_hex = hex ? dup_string(*hex) : nullptr;
     611         156 :   });
     612             : }
     613             : 
     614          13 : monero_result monero_daemon_get_tx_hexes(monero_daemon* daemon, const char* const* tx_hashes, size_t num_tx_hashes, bool prune, char** out_json) {
     615          13 :   reset_out(out_json);
     616          13 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     617          21 :   return guard([&] { *out_json = dup_string(json_of_strings(daemon->rpc->get_tx_hexes(string_array(tx_hashes, num_tx_hashes, "tx_hashes"), prune))); });
     618             : }
     619             : 
     620           9 : monero_result monero_daemon_get_miner_tx_sum(monero_daemon* daemon, uint64_t height, uint64_t num_blocks, char** out_json) {
     621           9 :   reset_out(out_json);
     622           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     623           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_miner_tx_sum(height, num_blocks)); });
     624             : }
     625             : 
     626           9 : monero_result monero_daemon_get_fee_estimate(monero_daemon* daemon, uint64_t grace_blocks, char** out_json) {
     627           9 :   reset_out(out_json);
     628           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     629           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_fee_estimate(grace_blocks)); });
     630             : }
     631             : 
     632          10 : monero_result monero_daemon_submit_tx_hex(monero_daemon* daemon, const char* tx_hex, bool do_not_relay, char** out_json) {
     633          10 :   reset_out(out_json);
     634          10 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     635          14 :   return guard([&] { *out_json = dup_json(daemon->rpc->submit_tx_hex(safe_str(tx_hex), do_not_relay)); });
     636             : }
     637             : 
     638         158 : monero_result monero_daemon_relay_tx_by_hash(monero_daemon* daemon, const char* tx_hash) {
     639         158 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     640         310 :   return guard([&] { daemon->rpc->relay_tx_by_hash(safe_str(tx_hash)); });
     641             : }
     642             : 
     643          11 : monero_result monero_daemon_relay_txs_by_hash(monero_daemon* daemon, const char* const* tx_hashes, size_t num_tx_hashes) {
     644          11 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     645          16 :   return guard([&] { daemon->rpc->relay_txs_by_hash(string_array(tx_hashes, num_tx_hashes, "tx_hashes")); });
     646             : }
     647             : 
     648             : // -------------------------------- TX POOL -----------------------------------
     649             : 
     650          12 : monero_result monero_daemon_get_tx_pool(monero_daemon* daemon, char** out_json) {
     651          12 :   reset_out(out_json);
     652          12 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     653          14 :   return guard([&] { *out_json = dup_json_blocks_released(daemon->rpc->get_tx_pool()); });
     654             : }
     655             : 
     656           9 : monero_result monero_daemon_get_tx_pool_hashes(monero_daemon* daemon, char** out_json) {
     657           9 :   reset_out(out_json);
     658           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     659           8 :   return guard([&] { *out_json = dup_string(json_of_strings(daemon->rpc->get_tx_pool_hashes())); });
     660             : }
     661             : 
     662          11 : monero_result monero_daemon_get_tx_pool_backlog(monero_daemon* daemon, char** out_json) {
     663          11 :   reset_out(out_json);
     664          11 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     665          12 :   return guard([&] { *out_json = dup_string(json_of_backlog(daemon->rpc->get_tx_pool_backlog())); });
     666             : }
     667             : 
     668           9 : monero_result monero_daemon_get_tx_pool_stats(monero_daemon* daemon, char** out_json) {
     669           9 :   reset_out(out_json);
     670           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     671           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_tx_pool_stats()); });
     672             : }
     673             : 
     674           7 : monero_result monero_daemon_flush_tx_pool(monero_daemon* daemon) {
     675           7 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     676           8 :   return guard([&] { daemon->rpc->flush_tx_pool(); });
     677             : }
     678             : 
     679          11 : monero_result monero_daemon_flush_tx_pool_hashes(monero_daemon* daemon, const char* const* tx_hashes, size_t num_tx_hashes) {
     680          11 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     681          16 :   return guard([&] { daemon->rpc->flush_tx_pool(string_array(tx_hashes, num_tx_hashes, "tx_hashes")); });
     682             : }
     683             : 
     684         158 : monero_result monero_daemon_flush_tx_pool_hash(monero_daemon* daemon, const char* tx_hash) {
     685         158 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     686         310 :   return guard([&] { daemon->rpc->flush_tx_pool(safe_str(tx_hash)); });
     687             : }
     688             : 
     689             : // --------------------------- KEY IMAGES AND OUTPUTS -------------------------
     690             : 
     691          10 : monero_result monero_daemon_get_key_image_spent_status(monero_daemon* daemon, const char* key_image, monero_key_image_spent_status* out_status) {
     692          10 :   if (!require(daemon, "daemon") || !require(out_status, "out_status")) return MONERO_ERROR;
     693          10 :   return guard([&] { *out_status = to_c_status(daemon->rpc->get_key_image_spent_status(safe_str(key_image))); });
     694             : }
     695             : 
     696          17 : monero_result monero_daemon_get_key_image_spent_statuses(monero_daemon* daemon, const char* const* key_images, size_t num_key_images, monero_key_image_spent_status** out_statuses, size_t* out_count) {
     697          17 :   reset_out(out_statuses);
     698          17 :   reset_out(out_count);
     699          17 :   if (!require(daemon, "daemon") || !require(out_statuses, "out_statuses") || !require(out_count, "out_count")) return MONERO_ERROR;
     700          18 :   return guard([&] {
     701           9 :     std::vector<monero_key_image_spent_status> statuses;
     702          15 :     for (monero::monero_key_image_spent_status status : daemon->rpc->get_key_image_spent_statuses(string_array(key_images, num_key_images, "key_images"))) {
     703           1 :       statuses.push_back(to_c_status(status));
     704           1 :     }
     705           1 :     *out_statuses = dup_array(statuses, out_count);
     706          18 :   });
     707             : }
     708             : 
     709          14 : monero_result monero_daemon_get_output_indices(monero_daemon* daemon, const char* tx_hash, uint64_t** out_indices, size_t* out_count) {
     710          14 :   reset_out(out_indices);
     711          14 :   reset_out(out_count);
     712          14 :   if (!require(daemon, "daemon") || !require(out_indices, "out_indices") || !require(out_count, "out_count")) return MONERO_ERROR;
     713          15 :   return guard([&] { *out_indices = dup_array(daemon->rpc->get_output_indices(safe_str(tx_hash)), out_count); });
     714             : }
     715             : 
     716         162 : monero_result monero_daemon_get_outputs(monero_daemon* daemon, const char* outputs_json, char** out_json) {
     717         162 :   reset_out(out_json);
     718         162 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     719         314 :   return guard([&] {
     720         157 :     std::vector<monero::monero_output> outputs;
     721         311 :     for (const auto& output : parse_list<monero::monero_output>(safe_str(outputs_json))) outputs.push_back(*output);
     722          66 :     *out_json = dup_string(json_of_list(daemon->rpc->get_outputs(outputs)));
     723         314 :   });
     724             : }
     725             : 
     726          16 : monero_result monero_daemon_get_output_histogram(monero_daemon* daemon, const uint64_t* amounts, size_t num_amounts, const int32_t* min_count, const int32_t* max_count, const bool* is_unlocked, const int32_t* recent_cutoff, char** out_json) {
     727          16 :   reset_out(out_json);
     728          16 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     729          22 :   return guard([&] {
     730          19 :     *out_json = dup_string(json_of_list(daemon->rpc->get_output_histogram(array_of(amounts, num_amounts, "amounts"), optional_of(min_count), optional_of(max_count), optional_of(is_unlocked), optional_of(recent_cutoff))));
     731          12 :   });
     732             : }
     733             : 
     734          15 : monero_result monero_daemon_get_output_distribution(monero_daemon* daemon, const uint64_t* amounts, size_t num_amounts, const bool* is_cumulative, const uint64_t* start_height, const uint64_t* end_height, char** out_json) {
     735          15 :   reset_out(out_json);
     736          15 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     737          20 :   return guard([&] {
     738          18 :     *out_json = dup_string(json_of_list(daemon->rpc->get_output_distribution(array_of(amounts, num_amounts, "amounts"), optional_of(is_cumulative), optional_of(start_height), optional_of(end_height))));
     739          11 :   });
     740             : }
     741             : 
     742             : // ---------------------------------- BANDWIDTH -------------------------------
     743             : 
     744           9 : monero_result monero_daemon_get_download_limit(monero_daemon* daemon, int32_t* out_limit) {
     745           9 :   if (!require(daemon, "daemon") || !require(out_limit, "out_limit")) return MONERO_ERROR;
     746           8 :   return guard([&] { *out_limit = daemon->rpc->get_download_limit(); });
     747             : }
     748             : 
     749           9 : monero_result monero_daemon_set_download_limit(monero_daemon* daemon, int32_t limit, int32_t* out_limit) {
     750           9 :   if (!require(daemon, "daemon") || !require(out_limit, "out_limit")) return MONERO_ERROR;
     751           8 :   return guard([&] { *out_limit = daemon->rpc->set_download_limit(limit); });
     752             : }
     753             : 
     754           9 : monero_result monero_daemon_reset_download_limit(monero_daemon* daemon, int32_t* out_limit) {
     755           9 :   if (!require(daemon, "daemon") || !require(out_limit, "out_limit")) return MONERO_ERROR;
     756           8 :   return guard([&] { *out_limit = daemon->rpc->reset_download_limit(); });
     757             : }
     758             : 
     759           9 : monero_result monero_daemon_get_upload_limit(monero_daemon* daemon, int32_t* out_limit) {
     760           9 :   if (!require(daemon, "daemon") || !require(out_limit, "out_limit")) return MONERO_ERROR;
     761           8 :   return guard([&] { *out_limit = daemon->rpc->get_upload_limit(); });
     762             : }
     763             : 
     764           9 : monero_result monero_daemon_set_upload_limit(monero_daemon* daemon, int32_t limit, int32_t* out_limit) {
     765           9 :   if (!require(daemon, "daemon") || !require(out_limit, "out_limit")) return MONERO_ERROR;
     766           8 :   return guard([&] { *out_limit = daemon->rpc->set_upload_limit(limit); });
     767             : }
     768             : 
     769           9 : monero_result monero_daemon_reset_upload_limit(monero_daemon* daemon, int32_t* out_limit) {
     770           9 :   if (!require(daemon, "daemon") || !require(out_limit, "out_limit")) return MONERO_ERROR;
     771           8 :   return guard([&] { *out_limit = daemon->rpc->reset_upload_limit(); });
     772             : }
     773             : 
     774             : // ----------------------------------- PEERS ----------------------------------
     775             : 
     776           9 : monero_result monero_daemon_get_peers(monero_daemon* daemon, char** out_json) {
     777           9 :   reset_out(out_json);
     778           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     779           8 :   return guard([&] { *out_json = dup_string(json_of_list(daemon->rpc->get_peers())); });
     780             : }
     781             : 
     782           9 : monero_result monero_daemon_get_known_peers(monero_daemon* daemon, char** out_json) {
     783           9 :   reset_out(out_json);
     784           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     785           8 :   return guard([&] { *out_json = dup_string(json_of_list(daemon->rpc->get_known_peers())); });
     786             : }
     787             : 
     788           9 : monero_result monero_daemon_get_public_peers(monero_daemon* daemon, bool include_offline, char** out_json) {
     789           9 :   reset_out(out_json);
     790           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     791           8 :   return guard([&] { *out_json = dup_string(json_of_list(daemon->rpc->get_public_peers(include_offline))); });
     792             : }
     793             : 
     794           7 : monero_result monero_daemon_set_outgoing_peer_limit(monero_daemon* daemon, int32_t limit) {
     795           7 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     796           8 :   return guard([&] { daemon->rpc->set_outgoing_peer_limit(limit); });
     797             : }
     798             : 
     799           7 : monero_result monero_daemon_set_incoming_peer_limit(monero_daemon* daemon, int32_t limit) {
     800           7 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     801           8 :   return guard([&] { daemon->rpc->set_incoming_peer_limit(limit); });
     802             : }
     803             : 
     804           9 : monero_result monero_daemon_get_peer_bans(monero_daemon* daemon, char** out_json) {
     805           9 :   reset_out(out_json);
     806           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     807           8 :   return guard([&] { *out_json = dup_string(json_of_list(daemon->rpc->get_peer_bans())); });
     808             : }
     809             : 
     810         163 : monero_result monero_daemon_set_peer_bans(monero_daemon* daemon, const char* bans_json) {
     811         163 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     812         509 :   return guard([&] { daemon->rpc->set_peer_bans(parse_list<monero::monero_ban>(safe_str(bans_json))); });
     813             : }
     814             : 
     815         161 : monero_result monero_daemon_set_peer_ban(monero_daemon* daemon, const char* ban_json) {
     816         161 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     817         316 :   return guard([&] {
     818         158 :     std::string json = safe_str(ban_json);
     819         158 :     check_json_depth(json);
     820         189 :     daemon->rpc->set_peer_ban(gen_utils::deserialize<monero::monero_ban>(json));
     821         316 :   });
     822             : }
     823             : 
     824          12 : monero_result monero_daemon_get_peer_ban(monero_daemon* daemon, const char* address, char** out_json) {
     825          12 :   reset_out(out_json);
     826          12 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     827          19 :   return guard([&] { *out_json = dup_json(daemon->rpc->get_peer_ban(safe_str(address))); });
     828             : }
     829             : 
     830             : // ----------------------------------- ADMIN ----------------------------------
     831             : 
     832           9 : monero_result monero_daemon_prune_blockchain(monero_daemon* daemon, bool check, char** out_json) {
     833           9 :   reset_out(out_json);
     834           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     835           8 :   return guard([&] { *out_json = dup_json(daemon->rpc->prune_blockchain(check)); });
     836             : }
     837             : 
     838           7 : monero_result monero_daemon_save_blockchain(monero_daemon* daemon) {
     839           7 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     840           8 :   return guard([&] { daemon->rpc->save_blockchain(); });
     841             : }
     842             : 
     843           9 : monero_result monero_daemon_pop_blocks(monero_daemon* daemon, uint64_t num_blocks, uint64_t* out_height) {
     844           9 :   if (!require(daemon, "daemon") || !require(out_height, "out_height")) return MONERO_ERROR;
     845           8 :   return guard([&] { *out_height = daemon->rpc->pop_blocks(num_blocks); });
     846             : }
     847             : 
     848           7 : monero_result monero_daemon_flush_cache(monero_daemon* daemon, bool bad_blocks) {
     849           7 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     850           8 :   return guard([&] { daemon->rpc->flush_cache(bad_blocks); });
     851             : }
     852             : 
     853         161 : monero_result monero_daemon_set_bootstrap_daemon(monero_daemon* daemon, const char* address, const char* username, const char* password, const char* proxy) {
     854         161 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     855         787 :   return guard([&] { daemon->rpc->set_bootstrap_daemon(safe_str(address), safe_str(username), safe_str(password), safe_str(proxy)); });
     856             : }
     857             : 
     858           7 : monero_result monero_daemon_remove_bootstrap_daemon(monero_daemon* daemon) {
     859           7 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     860           8 :   return guard([&] { daemon->rpc->remove_bootstrap_daemon(); });
     861             : }
     862             : 
     863           9 : monero_result monero_daemon_set_log_hash_rate(monero_daemon* daemon, bool is_visible) {
     864           9 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     865          12 :   return guard([&] { daemon->rpc->set_log_hash_rate(is_visible); });
     866             : }
     867             : 
     868           7 : monero_result monero_daemon_set_log_level(monero_daemon* daemon, int32_t level) {
     869           7 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     870           8 :   return guard([&] { daemon->rpc->set_log_level(level); });
     871             : }
     872             : 
     873          10 : monero_result monero_daemon_set_log_categories(monero_daemon* daemon, const char* categories, char** out_categories) {
     874          10 :   reset_out(out_categories);
     875          10 :   if (!require(daemon, "daemon") || !require(out_categories, "out_categories")) return MONERO_ERROR;
     876          14 :   return guard([&] { *out_categories = dup_string(daemon->rpc->set_log_categories(safe_str(categories))); });
     877             : }
     878             : 
     879           8 : monero_result monero_daemon_check_for_update(monero_daemon* daemon, char** out_json) {
     880           8 :   reset_out(out_json);
     881           8 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     882           6 :   return guard([&] { *out_json = dup_json(daemon->rpc->check_for_update()); });
     883             : }
     884             : 
     885           9 : monero_result monero_daemon_download_update(monero_daemon* daemon, const char* path, char** out_json) {
     886           9 :   reset_out(out_json);
     887           9 :   if (!require(daemon, "daemon") || !require(out_json, "out_json")) return MONERO_ERROR;
     888          12 :   return guard([&] { *out_json = dup_json(daemon->rpc->download_update(safe_str(path))); });
     889             : }
     890             : 
     891           6 : monero_result monero_daemon_stop(monero_daemon* daemon) {
     892           6 :   if (!require(daemon, "daemon")) return MONERO_ERROR;
     893           6 :   return guard([&] { daemon->rpc->stop(); });
     894             : }
     895             : 
     896             : } // extern "C"

Generated by: LCOV version 1.16