LCOV - code coverage report
Current view: top level - wallet - monero_c_wallet.cpp (source / functions) Hit Total Coverage
Test: monero-c coverage @ 3fdc3a1 Lines: 1028 1039 98.9 %
Date: 2026-10-18 16:42:22 Functions: 347 347 100.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             : #include "wallet/monero_c_wallet.h"
      24             : #include "common/monero_c_common.h"
      25             : #include "wallet/monero_wallet_full.h"
      26             : #include "wallet/monero_wallet_keys.h"
      27             : #include "wallet/monero_wallet_rpc.h"
      28             : #include "utils/gen_utils.h"
      29             : #include "utils/monero_utils.h"
      30             : 
      31             : #include "misc_language.h"
      32             : #include "rapidjson/document.h"
      33             : #include "rapidjson/stringbuffer.h"
      34             : #include "rapidjson/writer.h"
      35             : 
      36             : #include <boost/optional.hpp>
      37             : #include <memory>
      38             : #include <mutex>
      39             : #include <stdexcept>
      40             : #include <string>
      41             : #include <vector>
      42             : 
      43             : using monero_c::array_of;
      44             : using monero_c::check_json_depth;
      45             : using monero_c::check_mnemonic_length;
      46             : using monero_c::check_period_ms;
      47             : using monero_c::dup_string;
      48             : using monero_c::guard;
      49             : using monero_c::json_of_list;
      50             : using monero_c::json_of_strings;
      51             : using monero_c::network_of;
      52             : using monero_c::optional_of;
      53             : using monero_c::require;
      54             : using monero_c::reset_out;
      55             : using monero_c::safe_str;
      56             : using monero_c::string_array;
      57             : 
      58             : // holds the base class, so full and keys-only wallets share one handle type
      59             : // monero-cpp has `using namespace monero`, so the C wallet names are written with ::
      60             : struct monero_wallet {
      61             :   std::shared_ptr<monero::monero_wallet> wallet;
      62             :   bool regtest = false;  // monero-cpp has no regtest network, so the handle keeps the flag
      63             : };
      64             : 
      65             : namespace {
      66             : 
      67             : // the JSON that a listener gets for an output, in the shape of the results of monero_wallet_get_outputs(): the output in its tx, and the
      68             : // tx in its block, as {"blocks":[...]}. monero-cpp builds a tx for each notification, and the output points to it. The JSON has a copy
      69             : // that keeps the fields that the notification knows, the hash, the version, the unlock time and the flags of the tx, the height of its
      70             : // block when it has one, and the output with its amount and its indices, as a received output, or as an input if it is spent. The
      71             : // copies have no parents, so there is nothing to release, and the models of monero-cpp stay as they are
      72        1127 : std::string json_of_notification(const monero::monero_output_wallet& output, bool spent) {
      73        1127 :   std::shared_ptr<monero::monero_block> block = std::make_shared<monero::monero_block>();
      74        1127 :   std::shared_ptr<monero::monero_tx_wallet> tx = std::make_shared<monero::monero_tx_wallet>();
      75        1127 :   std::shared_ptr<monero::monero_output_wallet> copy = std::make_shared<monero::monero_output_wallet>();
      76        1127 :   copy->m_amount = output.m_amount;
      77        1127 :   copy->m_account_index = output.m_account_index;
      78        1127 :   copy->m_subaddress_index = output.m_subaddress_index;
      79        1127 :   copy->m_index = output.m_index;
      80        1127 :   copy->m_stealth_public_key = output.m_stealth_public_key;
      81        1127 :   copy->m_key_image = output.m_key_image;
      82        1127 :   copy->m_is_spent = output.m_is_spent;
      83        1127 :   copy->m_is_frozen = output.m_is_frozen;
      84        1127 :   if (output.m_tx != nullptr) {
      85        1127 :     tx->m_hash = output.m_tx->m_hash;
      86        1127 :     tx->m_version = output.m_tx->m_version;
      87        1127 :     tx->m_unlock_time = output.m_tx->m_unlock_time;
      88        1127 :     tx->m_is_confirmed = output.m_tx->m_is_confirmed;
      89        1127 :     tx->m_in_tx_pool = output.m_tx->m_in_tx_pool;
      90        1127 :     std::shared_ptr<monero::monero_tx_wallet> source = std::dynamic_pointer_cast<monero::monero_tx_wallet>(output.m_tx);
      91        1127 :     if (source != nullptr) tx->m_is_locked = source->m_is_locked;
      92        1127 :     if (output.m_tx->m_block != nullptr) {
      93        1127 :       block->m_height = output.m_tx->m_block->m_height;
      94        1127 :       block->m_timestamp = output.m_tx->m_block->m_timestamp;
      95             :     }
      96        1127 :   }
      97        1127 :   (spent ? tx->m_inputs : tx->m_outputs).push_back(copy);
      98        1127 :   block->m_txs.push_back(tx);
      99        3381 :   return "{\"blocks\":[" + block->serialize() + "]}";
     100        1127 : }
     101             : 
     102             : // forwards the wallet's notifications to the C callbacks of a listener
     103             : class wallet_listener_adapter : public monero::monero_wallet_listener {
     104             : public:
     105             :   ::monero_wallet_listener* owner = nullptr;  // the C handle, not the base class of the same name
     106             :   monero_wallet_listener_callbacks callbacks = {};
     107             : 
     108         508 :   void on_sync_progress(uint64_t height, uint64_t start_height, uint64_t end_height, double percent_done, const std::string& message) override {
     109         508 :     if (callbacks.on_sync_progress == nullptr) return;
     110           0 :     callbacks.on_sync_progress(callbacks.user_data, height, start_height, end_height, percent_done, message.c_str());
     111             :   }
     112             : 
     113         612 :   void on_new_block(uint64_t height) override {
     114         612 :     if (callbacks.on_new_block == nullptr) return;
     115           0 :     callbacks.on_new_block(callbacks.user_data, height);
     116             :   }
     117             : 
     118         507 :   void on_balances_changed(uint64_t new_balance, uint64_t new_unlocked_balance) override {
     119         507 :     if (callbacks.on_balances_changed == nullptr) return;
     120           0 :     callbacks.on_balances_changed(callbacks.user_data, new_balance, new_unlocked_balance);
     121             :   }
     122             : 
     123        1124 :   void on_output_received(const monero::monero_output_wallet& output) override {
     124        1124 :     if (callbacks.on_output_received == nullptr) return;
     125        1124 :     std::string json = json_of_notification(output, false);
     126        1124 :     callbacks.on_output_received(callbacks.user_data, json.c_str());
     127        1124 :   }
     128             : 
     129           3 :   void on_output_spent(const monero::monero_output_wallet& output) override {
     130           3 :     if (callbacks.on_output_spent == nullptr) return;
     131           3 :     std::string json = json_of_notification(output, true);
     132           3 :     callbacks.on_output_spent(callbacks.user_data, json.c_str());
     133           3 :   }
     134             : };
     135             : 
     136             : // copies a mnemonic argument, which can't be longer than a valid one
     137         224 : std::string mnemonic_of(const char* mnemonic) {
     138         224 :   std::string text(mnemonic);
     139         224 :   check_mnemonic_length(text);
     140         197 :   return text;
     141          27 : }
     142             : 
     143             : // monero-cpp reads most fields with get(), so every field is set, empty when unused
     144         419 : monero::monero_wallet_config make_config(const char* path, const char* password, int32_t network_type, const char* language) {
     145         419 :   monero::monero_wallet_config config;
     146         419 :   config.m_path = safe_str(path);
     147         419 :   config.m_password = safe_str(password);
     148         419 :   config.m_network_type = network_of<monero::monero_network_type>(network_type);
     149         409 :   std::string language_str = safe_str(language);
     150         409 :   config.m_language = language_str.empty() ? std::string("English") : language_str;
     151         409 :   config.m_server = nullptr;
     152         409 :   config.m_is_trusted_daemon = false;
     153         409 :   config.m_seed = std::string();
     154         409 :   config.m_seed_offset = std::string();
     155         409 :   config.m_primary_address = std::string();
     156         409 :   config.m_private_view_key = std::string();
     157         409 :   config.m_private_spend_key = std::string();
     158         409 :   config.m_is_multisig = false;
     159         409 :   config.m_regtest = network_type == MONERO_UTILS_NETWORK_REGTEST;
     160         818 :   return config;
     161         419 : }
     162             : 
     163             : // a new wallet starts from an estimate of the current date, which is past the height of a regtest chain. The keys file holds the
     164             : // height, and store() doesn't write that file, but a password does, so the same one is set again
     165           5 : void restart_from_genesis(monero::monero_wallet_full& wallet, const std::string& password) {
     166           5 :   wallet.set_restore_height(0);
     167           5 :   if (!wallet.get_path().empty()) wallet.change_password(password, password);
     168           5 : }
     169             : 
     170             : // takes ownership of a wallet returned by monero-cpp, created or opened on network_type
     171         141 : ::monero_wallet* wrap(std::unique_ptr<monero::monero_wallet> wallet, int32_t network_type) {
     172         141 :   std::unique_ptr<::monero_wallet> handle(new ::monero_wallet());
     173         141 :   handle->wallet = std::shared_ptr<monero::monero_wallet>(std::move(wallet));
     174         141 :   handle->regtest = network_type == MONERO_UTILS_NETWORK_REGTEST;
     175         282 :   return handle.release();
     176         141 : }
     177             : 
     178             : // monero-cpp's signature key enum has the same values as the C enum
     179         346 : monero::monero_message_signature_type to_signature_type(int32_t signature_type) {
     180         346 :   if (signature_type < MONERO_MESSAGE_SIGN_WITH_SPEND_KEY || signature_type > MONERO_MESSAGE_SIGN_WITH_VIEW_KEY) throw std::runtime_error("unknown signature type");
     181         340 :   return static_cast<monero::monero_message_signature_type>(signature_type);
     182             : }
     183             : 
     184             : // parses a JSON array into one monero-cpp model per element. The message is thrown if json is not an array
     185             : template <class T>
     186         225 : std::vector<std::shared_ptr<T>> parse_list(const std::string& json, const char* message) {
     187         225 :   check_json_depth(json);
     188         222 :   rapidjson::Document doc;
     189         222 :   if (doc.Parse(json.c_str()).HasParseError() || !doc.IsArray()) throw std::runtime_error(message);
     190          88 :   std::vector<std::shared_ptr<T>> items;
     191        2954 :   for (const auto& item : doc.GetArray()) {
     192        2860 :     rapidjson::StringBuffer buffer;
     193        2860 :     rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
     194        2860 :     item.Accept(writer);
     195        2866 :     items.push_back(T::deserialize(buffer.GetString()));
     196             :   }
     197         170 :   return items;
     198         225 : }
     199             : 
     200         225 : std::vector<std::shared_ptr<monero::monero_key_image>> parse_key_images(const std::string& json) {
     201         225 :   return parse_list<monero::monero_key_image>(json, "key images must be a JSON array");
     202             : }
     203             : 
     204             : // the metadata of a transaction created without relaying. monero-cpp can't deserialize a created transaction
     205             : // (it has an outgoingTransfer), so only the metadata is read
     206         114 : std::string tx_metadata_of(const rapidjson::Value& tx) {
     207         114 :   if (!tx.IsObject()) throw std::runtime_error("tx must be a JSON object");
     208         112 :   auto it = tx.FindMember("metadata");
     209         112 :   if (it == tx.MemberEnd() || !it->value.IsString() || it->value.GetStringLength() == 0) throw std::runtime_error("tx metadata is not initialized");
     210          97 :   return it->value.GetString();
     211             : }
     212             : 
     213             : // the txs in what a call that makes txs returns, {"txSets":[{"txs":[...]}]}, in one tx set, {"txs":[...]}, in a list of txs, or one
     214             : // tx, so that the result of a creation can be relayed as it is
     215         128 : std::vector<const rapidjson::Value*> txs_in(const rapidjson::Value& root) {
     216         128 :   std::vector<const rapidjson::Value*> txs;
     217          49 :   auto add_set = [&](const rapidjson::Value& set) {
     218          49 :     auto it = set.FindMember("txs");
     219          49 :     if (it == set.MemberEnd() || !it->value.IsArray()) return;
     220          96 :     for (const auto& tx : it->value.GetArray()) txs.push_back(&tx);
     221         128 :   };
     222         128 :   if (root.IsArray()) {
     223          11 :     for (const auto& tx : root.GetArray()) txs.push_back(&tx);
     224         118 :   } else if (root.IsObject()) {
     225         118 :     auto sets = root.FindMember("txSets");
     226         118 :     if (sets != root.MemberEnd() && sets->value.IsArray()) {
     227          98 :       for (const auto& set : sets->value.GetArray()) {
     228          48 :         if (set.IsObject()) add_set(set);
     229          50 :       }
     230          68 :     } else if (root.HasMember("txs")) {
     231           1 :       add_set(root);
     232             :     } else {
     233          67 :       txs.push_back(&root);
     234             :     }
     235             :   }
     236         256 :   return txs;
     237           0 : }
     238             : 
     239         172 : std::string tx_metadata_of_json(const std::string& json) {
     240         172 :   check_json_depth(json);
     241         169 :   rapidjson::Document doc;
     242         169 :   if (doc.Parse(json.c_str()).HasParseError() || !doc.IsObject()) throw std::runtime_error("tx must be a JSON object");
     243          71 :   std::vector<const rapidjson::Value*> txs = txs_in(doc);
     244          71 :   if (txs.size() != 1) throw std::runtime_error("the JSON holds " + std::to_string(txs.size()) + " txs, and one can be relayed");
     245         125 :   return tx_metadata_of(*txs[0]);
     246         184 : }
     247             : 
     248         167 : std::vector<std::string> tx_metadatas_of_json(const std::string& json) {
     249         167 :   check_json_depth(json);
     250         165 :   rapidjson::Document doc;
     251         165 :   if (doc.Parse(json.c_str()).HasParseError() || !(doc.IsArray() || (doc.IsObject() && (doc.HasMember("txSets") || doc.HasMember("txs"))))) throw std::runtime_error("txs must be a JSON array");
     252          57 :   std::vector<std::string> metadatas;
     253          98 :   for (const rapidjson::Value* tx : txs_in(doc)) metadatas.push_back(tx_metadata_of(*tx));
     254         106 :   return metadatas;
     255         169 : }
     256             : 
     257             : // parses a JSON object into a monero-cpp model. The name is the argument, for the error message
     258             : template <class T>
     259        1665 : std::shared_ptr<T> model_json(const char* json, const char* name) {
     260        1665 :   const char* p = json;
     261        1665 :   while (p != nullptr && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) p++;
     262        1665 :   if (p == nullptr || *p != '{') throw std::runtime_error(std::string(name) + " must be a JSON object");
     263        1619 :   std::string text(json);
     264        1619 :   check_json_depth(text);
     265        1599 :   std::shared_ptr<T> model = T::deserialize(text);
     266         525 :   if (model == nullptr) throw std::runtime_error(std::string(name) + " must be a JSON object");
     267        1050 :   return model;
     268        1619 : }
     269             : 
     270             : // reads the hex of a tx set and nothing else. monero-cpp can't parse the txs of a set that it created, because of their outgoing
     271             : // transfers, and describe_tx_set() only reads the hex, so the set of create_txs() can be passed as it is
     272         235 : std::shared_ptr<monero::monero_tx_set> tx_set_of_hex(const char* json) {
     273         235 :   std::string text(json);
     274         235 :   check_json_depth(text);
     275         230 :   rapidjson::Document doc;
     276         230 :   if (doc.Parse(text.c_str()).HasParseError() || !doc.IsObject()) throw std::runtime_error("tx_set must be a JSON object");
     277             :   // what a call that makes txs returns holds the tx sets, and one of them is described
     278         127 :   const rapidjson::Value* set = &doc;
     279         127 :   auto sets = doc.FindMember("txSets");
     280         127 :   if (sets != doc.MemberEnd()) {
     281          54 :     if (!sets->value.IsArray() || sets->value.Size() != 1 || !sets->value[0].IsObject()) throw std::runtime_error("txSets must hold one tx set");
     282          51 :     set = &sets->value[0];
     283             :   }
     284         124 :   std::shared_ptr<monero::monero_tx_set> tx_set = std::make_shared<monero::monero_tx_set>();
     285         360 :   auto copy_hex = [&](const char* name, boost::optional<std::string>& hex) {
     286         360 :     auto it = set->FindMember(name);
     287         360 :     if (it == set->MemberEnd() || it->value.IsNull()) return;
     288         181 :     if (!it->value.IsString()) throw std::runtime_error(std::string(name) + " must be a string");
     289         168 :     if (it->value.GetStringLength() > 0) hex = std::string(it->value.GetString(), it->value.GetStringLength());
     290         124 :   };
     291         124 :   copy_hex("unsignedTxHex", tx_set->m_unsigned_tx_hex);
     292         121 :   copy_hex("signedTxHex", tx_set->m_signed_tx_hex);
     293         115 :   copy_hex("multisigTxHex", tx_set->m_multisig_tx_hex);
     294         222 :   return tx_set;
     295         367 : }
     296             : 
     297             : // monero-cpp reads a null as the text "null", so a caller that writes its unset fields as null would set them
     298          41 : void drop_nulls(rapidjson::Value& object) {
     299         175 :   for (auto it = object.MemberBegin(); it != object.MemberEnd();) {
     300         134 :     if (it->value.IsNull()) it = object.EraseMember(it);
     301         129 :     else ++it;
     302             :   }
     303          41 : }
     304             : 
     305             : // parses the config of a wallet. monero-cpp has regtest as a flag on mainnet, and the ABI as the network type 3, so the type is
     306             : // translated before the parser reads it
     307          47 : std::shared_ptr<monero::monero_wallet_config> wallet_config_of(const char* json) {
     308          47 :   std::string text(json);
     309          47 :   check_json_depth(text);
     310          46 :   rapidjson::Document doc;
     311          46 :   if (doc.Parse(text.c_str()).HasParseError() || !doc.IsObject()) throw std::runtime_error("config must be a JSON object");
     312          35 :   drop_nulls(doc);
     313          35 :   auto server = doc.FindMember("server");
     314          35 :   if (server != doc.MemberEnd()) {
     315           7 :     if (!server->value.IsObject()) throw std::runtime_error("server must be a JSON object");
     316           6 :     drop_nulls(server->value);
     317             :   }
     318          34 :   auto network = doc.FindMember("networkType");
     319          34 :   if (network != doc.MemberEnd()) {
     320          27 :     if (!network->value.IsInt() || network->value.GetInt() < MONERO_UTILS_NETWORK_MAINNET || network->value.GetInt() > MONERO_UTILS_NETWORK_REGTEST) throw std::invalid_argument("unknown network type");
     321          25 :     if (network->value.GetInt() == MONERO_UTILS_NETWORK_REGTEST) {
     322           4 :       network->value.SetInt(MONERO_UTILS_NETWORK_MAINNET);
     323           4 :       auto regtest = doc.FindMember("regtest");
     324           4 :       if (regtest != doc.MemberEnd()) regtest->value.SetBool(true);
     325           3 :       else doc.AddMember("regtest", true, doc.GetAllocator());
     326             :     }
     327             :   }
     328          32 :   rapidjson::StringBuffer buffer;
     329          32 :   rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
     330          32 :   doc.Accept(writer);
     331          65 :   return monero::monero_wallet_config::deserialize(buffer.GetString());
     332          64 : }
     333             : 
     334             : // the JSON of the txs, transfers or outputs of a wallet, with the context that they have: every tx is in its block, which has its height
     335             : // and its timestamp, and a transfer or an output is in its tx. A tx that no block holds yet, in the pool or not relayed, is in one block
     336             : // that has no height. The helpers of monero-cpp build the blocks of txs and of transfers, and an output is found in its tx
     337         118 : std::string json_of_blocks(const std::vector<std::shared_ptr<monero::monero_block>>& blocks) {
     338         236 :   return "{\"blocks\":" + json_of_list(blocks) + "}";
     339             : }
     340             : 
     341             : // the tx sets that the txs belong to, each once and in the order of the txs. The calls that make txs return their sets, because the
     342             : // hex that signs or relays the txs is in the set
     343           8 : std::vector<std::shared_ptr<monero::monero_tx_set>> tx_sets_of(const std::vector<std::shared_ptr<monero::monero_tx_wallet>>& txs) {
     344           8 :   std::vector<std::shared_ptr<monero::monero_tx_set>> sets;
     345          13 :   for (const std::shared_ptr<monero::monero_tx_wallet>& tx : txs) {
     346           5 :     if (tx->m_tx_set == nullptr) throw std::runtime_error("tx has no tx set");
     347           5 :     if (std::find(sets.begin(), sets.end(), tx->m_tx_set) == sets.end()) sets.push_back(tx->m_tx_set);
     348             :   }
     349           8 :   return sets;
     350           0 : }
     351             : 
     352           8 : std::string json_of_tx_sets(const std::vector<std::shared_ptr<monero::monero_tx_set>>& sets) {
     353          16 :   return "{\"txSets\":" + json_of_list(sets) + "}";
     354             : }
     355             : 
     356          45 : std::vector<std::shared_ptr<monero::monero_block>> blocks_of_outputs(const std::vector<std::shared_ptr<monero::monero_output_wallet>>& outputs) {
     357          45 :   std::vector<std::shared_ptr<monero::monero_tx_wallet>> txs;
     358         385 :   for (const std::shared_ptr<monero::monero_output_wallet>& output : outputs) {
     359         340 :     std::shared_ptr<monero::monero_tx_wallet> tx = std::static_pointer_cast<monero::monero_tx_wallet>(output->m_tx);
     360         340 :     if (tx == nullptr) throw std::runtime_error("output has no tx");
     361         340 :     if (std::find(txs.begin(), txs.end(), tx) == txs.end()) txs.push_back(tx);
     362         340 :   }
     363          90 :   return monero_utils::get_blocks_from_txs(txs);
     364          45 : }
     365             : 
     366             : // releases a tx query that nothing else holds. A query that has a transfer, an input or an output query points to each one, and each one
     367             : // points back, so the queries keep each other alive. monero_utils::free() resets the links of the transfer and the output query, and the
     368             : // input query is reset here
     369         520 : void release_tx_query(const std::shared_ptr<monero::monero_tx_query>& query) {
     370         520 :   if (query == nullptr) return;
     371         501 :   if (query->m_input_query != nullptr) query->m_input_query->m_tx_query.reset();
     372         501 :   monero_utils::free(query);
     373             : }
     374             : 
     375             : // reads the query of a tx from its JSON, as monero_tx_query::deserialize() does. When the JSON fails after a transfer, an input or an
     376             : // output query was read, monero-cpp leaves them pointing at the query that it drops, so none is ever freed. The query is made here, and it
     377             : // is released if its parser throws
     378         713 : std::shared_ptr<monero::monero_tx_query> parse_tx_query(const std::string& json) {
     379         713 :   boost::property_tree::ptree node;
     380         713 :   gen_utils::deserialize(json, node);
     381         501 :   std::shared_ptr<monero::monero_tx_query> query = std::make_shared<monero::monero_tx_query>();
     382             :   try {
     383         501 :     monero::monero_tx_query::from_property_tree(node, query);
     384         147 :   } catch (...) {
     385         147 :     release_tx_query(query);
     386         147 :     throw;
     387         147 :   }
     388         708 :   return query;
     389         860 : }
     390             : 
     391             : // parses the query of the txs of get_txs()
     392         549 : std::shared_ptr<monero::monero_tx_query> tx_query_of(const char* json) {
     393         549 :   const char* p = json;
     394         549 :   while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
     395         549 :   if (*p != '{') throw std::runtime_error("query must be a JSON object");
     396         543 :   std::string text(json);
     397         543 :   check_json_depth(text);
     398         801 :   return parse_tx_query(text);
     399         543 : }
     400             : 
     401             : // the query of a transfer or of an output holds the query of its tx in "txQuery". monero-cpp reads the two as one tree that is rooted at
     402             : // a block, {"txs":[the query of the tx, with the query as its transferQuery or its outputQuery]}, and without it the query of the tx is
     403             : // lost, so the JSON of the tx is rebuilt that way
     404         401 : std::string tx_rooted_query(const char* json, const char* member) {
     405         401 :   std::string text(json);
     406         401 :   check_json_depth(text);
     407         394 :   rapidjson::Document doc;
     408         394 :   if (doc.Parse(text.c_str()).HasParseError() || !doc.IsObject()) throw std::runtime_error("query must be a JSON object");
     409         181 :   rapidjson::Document::AllocatorType& allocator = doc.GetAllocator();
     410         181 :   rapidjson::Value tx_query(rapidjson::kObjectType);
     411         181 :   auto it = doc.FindMember("txQuery");
     412         181 :   if (it != doc.MemberEnd()) {
     413         162 :     if (!it->value.IsObject()) throw std::runtime_error("txQuery must be a JSON object");
     414         155 :     tx_query = it->value;  // moves it
     415         155 :     doc.RemoveMember(it);
     416             :   }
     417         174 :   rapidjson::Value query;
     418         174 :   query.Swap(doc);
     419         174 :   tx_query.AddMember(rapidjson::StringRef(member), query, allocator);
     420         174 :   rapidjson::StringBuffer buffer;
     421         174 :   rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
     422         174 :   tx_query.Accept(writer);
     423         348 :   return buffer.GetString();
     424         628 : }
     425             : 
     426             : // reads the query of a tx that is in a block, as deserialize_from_block() of monero-cpp does
     427         401 : std::shared_ptr<monero::monero_tx_query> tx_query_in_block(const char* json, const char* member) {
     428         483 :   std::shared_ptr<monero::monero_tx_query> tx_query = parse_tx_query(tx_rooted_query(json, member));
     429          92 :   std::shared_ptr<monero::monero_block> block = std::make_shared<monero::monero_block>();
     430          92 :   block->m_txs.push_back(tx_query);
     431          92 :   tx_query->m_block = block;
     432         184 :   return tx_query;
     433          92 : }
     434             : 
     435             : // parses an optional query of transfers or of outputs. NULL gives the default query, which matches everything
     436         207 : std::shared_ptr<monero::monero_transfer_query> transfer_query_of(const char* json) {
     437         207 :   if (json == nullptr) return std::make_shared<monero::monero_transfer_query>();
     438         201 :   std::shared_ptr<monero::monero_tx_query> tx_query = tx_query_in_block(json, "transferQuery");
     439          44 :   std::shared_ptr<monero::monero_transfer_query> transfer_query = tx_query->m_transfer_query == nullptr ? std::make_shared<monero::monero_transfer_query>() : tx_query->m_transfer_query;
     440          44 :   transfer_query->m_tx_query = tx_query;
     441          44 :   return transfer_query;
     442          44 : }
     443             : 
     444         207 : std::shared_ptr<monero::monero_output_query> output_query_of(const char* json) {
     445         207 :   if (json == nullptr) return std::make_shared<monero::monero_output_query>();
     446         200 :   std::shared_ptr<monero::monero_tx_query> tx_query = tx_query_in_block(json, "outputQuery");
     447          48 :   std::shared_ptr<monero::monero_output_query> input_query = tx_query->m_input_query == nullptr ? std::make_shared<monero::monero_output_query>() : tx_query->m_input_query;
     448          48 :   input_query->m_tx_query = tx_query;
     449          48 :   std::shared_ptr<monero::monero_output_query> output_query = tx_query->m_output_query == nullptr ? std::make_shared<monero::monero_output_query>() : tx_query->m_output_query;
     450          48 :   output_query->m_tx_query = tx_query;
     451          48 :   return output_query;
     452          48 : }
     453             : 
     454             : // shares a wallet with a new handle, for a wallet that is also owned by another handle
     455           6 : ::monero_wallet* wrap_shared(std::shared_ptr<monero::monero_wallet> wallet) {
     456           6 :   std::unique_ptr<::monero_wallet> handle(new ::monero_wallet());
     457           6 :   handle->wallet = std::move(wallet);
     458          12 :   return handle.release();
     459           6 : }
     460             : 
     461             : // the RPC methods are only on monero_wallet_rpc, so the handle must hold an RPC wallet
     462         678 : monero::monero_wallet_rpc& as_rpc(::monero_wallet* wallet) {
     463         678 :   monero::monero_wallet_rpc* rpc = dynamic_cast<monero::monero_wallet_rpc*>(wallet->wallet.get());
     464         678 :   if (rpc == nullptr) throw std::runtime_error("not an RPC wallet");
     465         565 :   return *rpc;
     466             : }
     467             : 
     468             : // a connection to a monero-wallet-rpc server, to open or create wallets on it
     469          28 : std::shared_ptr<monero::monero_wallet_rpc> rpc_client(const char* uri, const char* username, const char* password) {
     470          72 :   return std::make_shared<monero::monero_wallet_rpc>(std::string(uri), safe_str(username), safe_str(password));
     471             : }
     472             : 
     473             : // the config of a wallet that monero-wallet-rpc creates, with the name, the password and the language
     474          24 : std::shared_ptr<monero::monero_wallet_config> rpc_config(const char* name, const char* wallet_password, const char* language) {
     475          24 :   auto config = std::make_shared<monero::monero_wallet_config>();
     476          24 :   config->m_path = std::string(name);
     477          24 :   config->m_password = safe_str(wallet_password);
     478          24 :   if (language != nullptr && language[0] != '\0') config->m_language = std::string(language);
     479          24 :   return config;
     480           0 : }
     481             : 
     482             : // creates a wallet on a monero-wallet-rpc server and returns a handle to it
     483          22 : ::monero_wallet* create_rpc_wallet(const char* uri, const char* username, const char* password, const std::shared_ptr<monero::monero_wallet_config>& config) {
     484          22 :   std::shared_ptr<monero::monero_wallet_rpc> rpc = rpc_client(uri, username, password);
     485          16 :   rpc->create_wallet(config);
     486           0 :   return wrap_shared(rpc);
     487          16 : }
     488             : 
     489             : } // namespace
     490             : 
     491             : struct monero_wallet_listener {
     492             :   wallet_listener_adapter adapter;
     493             :   std::weak_ptr<monero::monero_wallet> registered_with;
     494             :   std::mutex registration_mutex;  // guards registered_with, never held across a monero-cpp call
     495             : };
     496             : 
     497             : namespace {
     498             : 
     499             : // monero-cpp adds the listener for a sync and removes it after, so a listener that the handle
     500             : // already tracks uses the plain sync. Otherwise the removal would unregister it
     501          20 : monero::monero_sync_result sync_with_listener(::monero_wallet* wallet, ::monero_wallet_listener* listener, const uint64_t* start_height) {
     502          20 :   std::shared_ptr<monero::monero_wallet> previous;
     503             :   {
     504          20 :     std::lock_guard<std::mutex> lock(listener->registration_mutex);
     505          20 :     previous = listener->registered_with.lock();
     506          20 :   }
     507          20 :   if (previous && previous != wallet->wallet) throw std::runtime_error("listener is already registered with another wallet");
     508          19 :   if (previous) return start_height ? wallet->wallet->sync(*start_height) : wallet->wallet->sync();
     509          18 :   return start_height ? wallet->wallet->sync(*start_height, listener->adapter) : wallet->wallet->sync(listener->adapter);
     510          20 : }
     511             : 
     512             : } // namespace
     513             : 
     514             : extern "C" {
     515             : 
     516             : // -------------------------------- WALLET ------------------------------------
     517             : 
     518          27 : monero_result monero_wallet_create_random(const char* path, const char* password, int32_t network_type, const char* language, ::monero_wallet** out_wallet) {
     519          27 :   reset_out(out_wallet);
     520          27 :   if (!require(path, "path") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
     521          44 :   return guard([&] {
     522          22 :     monero::monero_wallet_config config = make_config(path, password, network_type, language);
     523          20 :     std::unique_ptr<monero::monero_wallet_full> wallet(monero::monero_wallet_full::create_wallet(config));
     524          18 :     if (network_type == MONERO_UTILS_NETWORK_REGTEST) restart_from_genesis(*wallet, *config.m_password);
     525          18 :     *out_wallet = wrap(std::move(wallet), network_type);
     526          41 :   });
     527             : }
     528             : 
     529          44 : monero_result monero_wallet_create_from_seed(const char* path, const char* password, int32_t network_type, const char* seed, const char* seed_offset, uint64_t restore_height, const char* language, ::monero_wallet** out_wallet) {
     530          44 :   reset_out(out_wallet);
     531          44 :   if (!require(path, "path") || !require(seed, "seed") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
     532          78 :   return guard([&] {
     533          39 :     monero::monero_wallet_config config = make_config(path, password, network_type, language);
     534          38 :     config.m_seed = mnemonic_of(seed);
     535          36 :     config.m_seed_offset = safe_str(seed_offset);
     536          36 :     config.m_restore_height = restore_height;
     537          37 :     *out_wallet = wrap(std::unique_ptr<monero::monero_wallet_full>(monero::monero_wallet_full::create_wallet(config)), network_type);
     538          77 :   });
     539             : }
     540             : 
     541          16 : monero_result monero_wallet_create_from_keys(const char* path, const char* password, int32_t network_type, const char* address, const char* private_view_key, const char* private_spend_key, uint64_t restore_height, const char* language, ::monero_wallet** out_wallet) {
     542          16 :   reset_out(out_wallet);
     543          16 :   if (!require(path, "path") || !require(address, "address") || !require(private_view_key, "private_view_key") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
     544          22 :   return guard([&] {
     545          11 :     monero::monero_wallet_config config = make_config(path, password, network_type, language);
     546          10 :     config.m_primary_address = std::string(address);
     547          10 :     config.m_private_view_key = std::string(private_view_key);
     548          10 :     config.m_private_spend_key = safe_str(private_spend_key);
     549          10 :     config.m_restore_height = restore_height;
     550          14 :     *out_wallet = wrap(std::unique_ptr<monero::monero_wallet_full>(monero::monero_wallet_full::create_wallet(config)), network_type);
     551          21 :   });
     552             : }
     553             : 
     554          53 : monero_result monero_wallet_create(const char* config_json, ::monero_wallet** out_wallet) {
     555          53 :   reset_out(out_wallet);
     556          53 :   if (!require(config_json, "config_json") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
     557          94 :   return guard([&] {
     558          47 :     std::shared_ptr<monero::monero_wallet_config> config = wallet_config_of(config_json);
     559          31 :     bool has_seed = config->m_seed != boost::none && !config->m_seed->empty();
     560          55 :     bool has_keys = (config->m_primary_address != boost::none && !config->m_primary_address->empty()) || (config->m_private_view_key != boost::none && !config->m_private_view_key->empty()) ||
     561          24 :                     (config->m_private_spend_key != boost::none && !config->m_private_spend_key->empty());
     562          31 :     if (has_seed && has_keys) throw std::invalid_argument("Wallet may be initialized with a seed or keys but not both");
     563             :     // the seed of a multisig wallet is hex data, which can be longer than any mnemonic
     564          30 :     if (has_seed && !(config->m_is_multisig != boost::none && *config->m_is_multisig)) check_mnemonic_length(*config->m_seed);
     565          30 :     bool regtest = config->m_regtest != boost::none && *config->m_regtest;
     566          45 :     std::unique_ptr<monero::monero_wallet_full> wallet(monero::monero_wallet_full::create_wallet(*config));
     567          15 :     if (regtest && !has_seed && !has_keys) restart_from_genesis(*wallet, config->m_password != boost::none ? *config->m_password : std::string());
     568          15 :     *out_wallet = wrap(std::move(wallet), regtest ? MONERO_UTILS_NETWORK_REGTEST : static_cast<int32_t>(*config->m_network_type));
     569          78 :   });
     570             : }
     571             : 
     572         167 : monero_result monero_wallet_open(const char* path, const char* password, int32_t network_type, ::monero_wallet** out_wallet) {
     573         167 :   reset_out(out_wallet);
     574         167 :   if (!require(path, "path") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
     575         326 :   return guard([&] {
     576         631 :     *out_wallet = wrap(std::unique_ptr<monero::monero_wallet_full>(monero::monero_wallet_full::open_wallet(std::string(path), safe_str(password), network_of<monero::monero_network_type>(network_type), network_type == MONERO_UTILS_NETWORK_REGTEST)), network_type);
     577         168 :   });
     578             : }
     579             : 
     580         159 : monero_result monero_wallet_exists(const char* path, bool* out_exists) {
     581         159 :   if (!require(path, "path") || !require(out_exists, "out_exists")) return MONERO_ERROR;
     582         312 :   return guard([&] { *out_exists = monero::monero_wallet_full::wallet_exists(std::string(path)); });
     583             : }
     584             : 
     585           6 : monero_result monero_wallet_get_seed_languages(char** out_json) {
     586           6 :   reset_out(out_json);
     587           6 :   if (!require(out_json, "out_json")) return MONERO_ERROR;
     588           8 :   return guard([&] { *out_json = dup_string(json_of_strings(monero::monero_wallet_full::get_seed_languages())); });
     589             : }
     590             : 
     591          13 : monero_result monero_wallet_keys_create_random(int32_t network_type, const char* language, ::monero_wallet** out_wallet) {
     592          13 :   reset_out(out_wallet);
     593          13 :   if (!require(out_wallet, "out_wallet")) return MONERO_ERROR;
     594          22 :   return guard([&] {
     595          11 :     monero::monero_wallet_config config = make_config(nullptr, nullptr, network_type, language);
     596           8 :     *out_wallet = wrap(std::unique_ptr<monero::monero_wallet>(monero::monero_wallet_keys::create_wallet_random(config)), network_type);
     597          19 :   });
     598             : }
     599             : 
     600         182 : monero_result monero_wallet_keys_create_from_seed(int32_t network_type, const char* seed, const char* seed_offset, const char* language, ::monero_wallet** out_wallet) {
     601         182 :   reset_out(out_wallet);
     602         182 :   if (!require(seed, "seed") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
     603         356 :   return guard([&] {
     604         178 :     monero::monero_wallet_config config = make_config(nullptr, nullptr, network_type, language);
     605         177 :     config.m_seed = mnemonic_of(seed);
     606         154 :     config.m_seed_offset = safe_str(seed_offset);
     607         154 :     *out_wallet = wrap(std::unique_ptr<monero::monero_wallet>(monero::monero_wallet_keys::create_wallet_from_seed(config)), network_type);
     608         355 :   });
     609             : }
     610             : 
     611         163 : monero_result monero_wallet_keys_create_from_keys(int32_t network_type, const char* address, const char* private_view_key, const char* private_spend_key, const char* language, ::monero_wallet** out_wallet) {
     612         163 :   reset_out(out_wallet);
     613         163 :   if (!require(address, "address") || !require(private_view_key, "private_view_key") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
     614         316 :   return guard([&] {
     615         158 :     monero::monero_wallet_config config = make_config(nullptr, nullptr, network_type, language);
     616         157 :     config.m_primary_address = std::string(address);
     617         157 :     config.m_private_view_key = std::string(private_view_key);
     618         157 :     config.m_private_spend_key = safe_str(private_spend_key);
     619         157 :     *out_wallet = wrap(std::unique_ptr<monero::monero_wallet>(monero::monero_wallet_keys::create_wallet_from_keys(config)), network_type);
     620         315 :   });
     621             : }
     622             : 
     623          33 : monero_result monero_wallet_save(::monero_wallet* wallet) {
     624          33 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
     625          46 :   return guard([&] { wallet->wallet->save(); });
     626             : }
     627             : 
     628          34 : monero_result monero_wallet_close(::monero_wallet* wallet, bool save) {
     629          34 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
     630          50 :   return guard([&] { wallet->wallet->close(save); });
     631             : }
     632             : 
     633          26 : monero_result monero_wallet_request_shutdown(::monero_wallet* wallet) {
     634          26 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
     635          34 :   return guard([&] {
     636             :     // only the full wallet has a sync and requests to abort. It is the one call that runs while another thread is inside the wallet
     637          17 :     monero::monero_wallet_full* full = dynamic_cast<monero::monero_wallet_full*>(wallet->wallet.get());
     638          17 :     if (full == nullptr) throw std::runtime_error("request_shutdown() not supported");
     639           9 :     full->request_shutdown();
     640          26 :   });
     641             : }
     642             : 
     643         156 : void monero_wallet_free(::monero_wallet* wallet) {
     644         156 :   delete wallet;
     645         156 : }
     646             : 
     647          31 : monero_result monero_wallet_get_network_type(::monero_wallet* wallet, monero_utils_network_type* out_network_type) {
     648          31 :   if (!require(wallet, "wallet") || !require(out_network_type, "out_network_type")) return MONERO_ERROR;
     649          36 :   return guard([&] {
     650             :     // monero-cpp runs regtest as mainnet with a flag, so the handle tells regtest apart
     651          18 :     *out_network_type = wallet->regtest ? MONERO_UTILS_NETWORK_REGTEST : static_cast<monero_utils_network_type>(wallet->wallet->get_network_type());
     652          18 :   });
     653             : }
     654             : 
     655          33 : monero_result monero_wallet_is_view_only(::monero_wallet* wallet, bool* out_view_only) {
     656          33 :   if (!require(wallet, "wallet") || !require(out_view_only, "out_view_only")) return MONERO_ERROR;
     657          40 :   return guard([&] { *out_view_only = wallet->wallet->is_view_only(); });
     658             : }
     659             : 
     660          32 : monero_result monero_wallet_is_multisig(::monero_wallet* wallet, bool* out_multisig) {
     661          32 :   if (!require(wallet, "wallet") || !require(out_multisig, "out_multisig")) return MONERO_ERROR;
     662          38 :   return guard([&] { *out_multisig = wallet->wallet->is_multisig(); });
     663             : }
     664             : 
     665          34 : monero_result monero_wallet_get_seed(::monero_wallet* wallet, char** out_seed) {
     666          34 :   reset_out(out_seed);
     667          34 :   if (!require(wallet, "wallet") || !require(out_seed, "out_seed")) return MONERO_ERROR;
     668          40 :   return guard([&] { *out_seed = dup_string(wallet->wallet->get_seed()); });
     669             : }
     670             : 
     671          30 : monero_result monero_wallet_get_seed_language(::monero_wallet* wallet, char** out_language) {
     672          30 :   reset_out(out_language);
     673          30 :   if (!require(wallet, "wallet") || !require(out_language, "out_language")) return MONERO_ERROR;
     674          34 :   return guard([&] { *out_language = dup_string(wallet->wallet->get_seed_language()); });
     675             : }
     676             : 
     677         403 : monero_result monero_wallet_get_primary_address(::monero_wallet* wallet, char** out_address) {
     678         403 :   reset_out(out_address);
     679         403 :   if (!require(wallet, "wallet") || !require(out_address, "out_address")) return MONERO_ERROR;
     680         780 :   return guard([&] { *out_address = dup_string(wallet->wallet->get_primary_address()); });
     681             : }
     682             : 
     683          33 : monero_result monero_wallet_get_private_view_key(::monero_wallet* wallet, char** out_key) {
     684          33 :   reset_out(out_key);
     685          33 :   if (!require(wallet, "wallet") || !require(out_key, "out_key")) return MONERO_ERROR;
     686          40 :   return guard([&] { *out_key = dup_string(wallet->wallet->get_private_view_key()); });
     687             : }
     688             : 
     689          35 : monero_result monero_wallet_get_private_spend_key(::monero_wallet* wallet, char** out_key) {
     690          35 :   reset_out(out_key);
     691          35 :   if (!require(wallet, "wallet") || !require(out_key, "out_key")) return MONERO_ERROR;
     692          44 :   return guard([&] { *out_key = dup_string(wallet->wallet->get_private_spend_key()); });
     693             : }
     694             : 
     695          31 : monero_result monero_wallet_get_height(::monero_wallet* wallet, uint64_t* out_height) {
     696          31 :   if (!require(wallet, "wallet") || !require(out_height, "out_height")) return MONERO_ERROR;
     697          34 :   return guard([&] { *out_height = wallet->wallet->get_height(); });
     698             : }
     699             : 
     700          30 : monero_result monero_wallet_get_balance(::monero_wallet* wallet, uint64_t* out_balance) {
     701          30 :   if (!require(wallet, "wallet") || !require(out_balance, "out_balance")) return MONERO_ERROR;
     702          34 :   return guard([&] { *out_balance = wallet->wallet->get_balance(); });
     703             : }
     704             : 
     705          27 : monero_result monero_wallet_get_unlocked_balance(::monero_wallet* wallet, uint64_t* out_balance) {
     706          27 :   if (!require(wallet, "wallet") || !require(out_balance, "out_balance")) return MONERO_ERROR;
     707          28 :   return guard([&] { *out_balance = wallet->wallet->get_unlocked_balance(); });
     708             : }
     709             : 
     710          28 : monero_result monero_wallet_get_account_balance(::monero_wallet* wallet, uint32_t account_idx, uint64_t* out_balance) {
     711          28 :   if (!require(wallet, "wallet") || !require(out_balance, "out_balance")) return MONERO_ERROR;
     712          30 :   return guard([&] { *out_balance = wallet->wallet->get_balance(account_idx); });
     713             : }
     714             : 
     715          28 : monero_result monero_wallet_get_subaddress_balance(::monero_wallet* wallet, uint32_t account_idx, uint32_t subaddress_idx, uint64_t* out_balance) {
     716          28 :   if (!require(wallet, "wallet") || !require(out_balance, "out_balance")) return MONERO_ERROR;
     717          30 :   return guard([&] { *out_balance = wallet->wallet->get_balance(account_idx, subaddress_idx); });
     718             : }
     719             : 
     720          28 : monero_result monero_wallet_get_account_unlocked_balance(::monero_wallet* wallet, uint32_t account_idx, uint64_t* out_balance) {
     721          28 :   if (!require(wallet, "wallet") || !require(out_balance, "out_balance")) return MONERO_ERROR;
     722          30 :   return guard([&] { *out_balance = wallet->wallet->get_unlocked_balance(account_idx); });
     723             : }
     724             : 
     725          28 : monero_result monero_wallet_get_subaddress_unlocked_balance(::monero_wallet* wallet, uint32_t account_idx, uint32_t subaddress_idx, uint64_t* out_balance) {
     726          28 :   if (!require(wallet, "wallet") || !require(out_balance, "out_balance")) return MONERO_ERROR;
     727          30 :   return guard([&] { *out_balance = wallet->wallet->get_unlocked_balance(account_idx, subaddress_idx); });
     728             : }
     729             : 
     730          62 : monero_result monero_wallet_get_address(::monero_wallet* wallet, uint32_t account_idx, uint32_t subaddress_idx, char** out_address) {
     731          62 :   reset_out(out_address);
     732          62 :   if (!require(wallet, "wallet") || !require(out_address, "out_address")) return MONERO_ERROR;
     733          98 :   return guard([&] { *out_address = dup_string(wallet->wallet->get_address(account_idx, subaddress_idx)); });
     734             : }
     735             : 
     736         186 : monero_result monero_wallet_get_integrated_address(::monero_wallet* wallet, const char* standard_address, const char* payment_id, char** out_json) {
     737         186 :   reset_out(out_json);
     738         186 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     739         660 :   return guard([&] { *out_json = dup_string(wallet->wallet->get_integrated_address(safe_str(standard_address), safe_str(payment_id)).serialize()); });
     740             : }
     741             : 
     742         181 : monero_result monero_wallet_decode_integrated_address(::monero_wallet* wallet, const char* integrated_address, char** out_json) {
     743         181 :   reset_out(out_json);
     744         181 :   if (!require(wallet, "wallet") || !require(integrated_address, "integrated_address") || !require(out_json, "out_json")) return MONERO_ERROR;
     745         654 :   return guard([&] { *out_json = dup_string(wallet->wallet->decode_integrated_address(std::string(integrated_address)).serialize()); });
     746             : }
     747             : 
     748          27 : monero_result monero_wallet_get_account(::monero_wallet* wallet, uint32_t account_idx, bool include_subaddresses, char** out_json) {
     749          27 :   reset_out(out_json);
     750          27 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     751          28 :   return guard([&] { *out_json = dup_string(wallet->wallet->get_account(account_idx, include_subaddresses).serialize()); });
     752             : }
     753             : 
     754          38 : monero_result monero_wallet_get_subaddresses(::monero_wallet* wallet, uint32_t account_idx, const uint32_t* subaddress_indices, size_t num_subaddress_indices, char** out_json) {
     755          38 :   reset_out(out_json);
     756          38 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     757          48 :   return guard([&] {
     758          24 :     auto indices = array_of(subaddress_indices, num_subaddress_indices, "subaddress_indices");
     759          19 :     *out_json = dup_string(json_of_list(wallet->wallet->get_subaddresses(account_idx, indices)));
     760          43 :   });
     761             : }
     762             : 
     763         363 : monero_result monero_wallet_sign_message(::monero_wallet* wallet, const char* message, int32_t signature_type, uint32_t account_idx, uint32_t subaddress_idx, char** out_signature) {
     764         363 :   reset_out(out_signature);
     765         363 :   if (!require(wallet, "wallet") || !require(message, "message") || !require(out_signature, "out_signature")) return MONERO_ERROR;
     766         704 :   return guard([&] { *out_signature = dup_string(wallet->wallet->sign_message(std::string(message), to_signature_type(signature_type), account_idx, subaddress_idx)); });
     767             : }
     768             : 
     769         521 : monero_result monero_wallet_verify_message(::monero_wallet* wallet, const char* message, const char* address, const char* signature, char** out_json) {
     770         521 :   reset_out(out_json);
     771         521 :   if (!require(wallet, "wallet") || !require(message, "message") || !require(address, "address") || !require(signature, "signature") || !require(out_json, "out_json")) return MONERO_ERROR;
     772        1898 :   return guard([&] { *out_json = dup_string(wallet->wallet->verify_message(std::string(message), std::string(address), std::string(signature)).serialize()); });
     773             : }
     774             : 
     775         184 : monero_result monero_wallet_add_address_book_entry(::monero_wallet* wallet, const char* address, const char* description, uint64_t* out_index) {
     776         184 :   if (!require(wallet, "wallet") || !require(address, "address") || !require(out_index, "out_index")) return MONERO_ERROR;
     777         662 :   return guard([&] { *out_index = wallet->wallet->add_address_book_entry(std::string(address), safe_str(description)); });
     778             : }
     779             : 
     780          30 : monero_result monero_wallet_edit_address_book_entry(::monero_wallet* wallet, uint64_t index, const char* address, const char* description) {
     781          30 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
     782          59 :   return guard([&] { wallet->wallet->edit_address_book_entry(index, address != nullptr, safe_str(address), description != nullptr, safe_str(description)); });
     783             : }
     784             : 
     785          22 : monero_result monero_wallet_delete_address_book_entry(::monero_wallet* wallet, uint64_t index) {
     786          22 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
     787          26 :   return guard([&] { wallet->wallet->delete_address_book_entry(index); });
     788             : }
     789             : 
     790          38 : monero_result monero_wallet_get_address_book_entries(::monero_wallet* wallet, const uint64_t* indices, size_t num_indices, char** out_json) {
     791          38 :   reset_out(out_json);
     792          38 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     793          63 :   return guard([&] { *out_json = dup_string(json_of_list(wallet->wallet->get_address_book_entries(array_of(indices, num_indices, "indices")))); });
     794             : }
     795             : 
     796          31 : monero_result monero_wallet_change_password(::monero_wallet* wallet, const char* old_password, const char* new_password) {
     797          31 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
     798          64 :   return guard([&] { wallet->wallet->change_password(safe_str(old_password), safe_str(new_password)); });
     799             : }
     800             : 
     801         180 : monero_result monero_wallet_create_account(::monero_wallet* wallet, const char* label, char** out_json) {
     802         180 :   reset_out(out_json);
     803         180 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     804         348 :   return guard([&] { *out_json = dup_string(wallet->wallet->create_account(safe_str(label)).serialize()); });
     805             : }
     806             : 
     807         210 : monero_result monero_wallet_create_subaddress(::monero_wallet* wallet, uint32_t account_idx, const char* label, char** out_json) {
     808         210 :   reset_out(out_json);
     809         210 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     810         408 :   return guard([&] { *out_json = dup_string(wallet->wallet->create_subaddress(account_idx, safe_str(label)).serialize()); });
     811             : }
     812             : 
     813         207 : monero_result monero_wallet_get_accounts(::monero_wallet* wallet, bool include_subaddresses, char** out_json) {
     814         207 :   reset_out(out_json);
     815         207 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     816         388 :   return guard([&] { *out_json = dup_string(json_of_list(wallet->wallet->get_accounts(include_subaddresses))); });
     817             : }
     818             : 
     819         180 : monero_result monero_wallet_get_address_index(::monero_wallet* wallet, const char* address, char** out_json) {
     820         180 :   reset_out(out_json);
     821         180 :   if (!require(wallet, "wallet") || !require(address, "address") || !require(out_json, "out_json")) return MONERO_ERROR;
     822         626 :   return guard([&] { *out_json = dup_string(wallet->wallet->get_address_index(std::string(address)).serialize()); });
     823             : }
     824             : 
     825          26 : monero_result monero_wallet_get_subaddress(::monero_wallet* wallet, uint32_t account_idx, uint32_t subaddress_idx, char** out_json) {
     826          26 :   reset_out(out_json);
     827          26 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     828          26 :   return guard([&] { *out_json = dup_string(wallet->wallet->get_subaddress(account_idx, subaddress_idx).serialize()); });
     829             : }
     830             : 
     831          26 : monero_result monero_wallet_set_subaddress_label(::monero_wallet* wallet, uint32_t account_idx, uint32_t subaddress_idx, const char* label) {
     832          26 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
     833          34 :   return guard([&] { wallet->wallet->set_subaddress_label(account_idx, subaddress_idx, safe_str(label)); });
     834             : }
     835             : 
     836         180 : monero_result monero_wallet_set_account_tag_label(::monero_wallet* wallet, const char* tag, const char* label) {
     837         180 :   if (!require(wallet, "wallet") || !require(tag, "tag")) return MONERO_ERROR;
     838         668 :   return guard([&] { wallet->wallet->set_account_tag_label(std::string(tag), safe_str(label)); });
     839             : }
     840             : 
     841          26 : monero_result monero_wallet_get_account_tags(::monero_wallet* wallet, char** out_json) {
     842          26 :   reset_out(out_json);
     843          26 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     844          26 :   return guard([&] { *out_json = dup_string(json_of_list(wallet->wallet->get_account_tags())); });
     845             : }
     846             : 
     847          34 : monero_result monero_wallet_tag_accounts(::monero_wallet* wallet, const char* tag, const uint32_t* account_indices, size_t num_account_indices) {
     848          34 :   if (!require(wallet, "wallet") || !require(tag, "tag")) return MONERO_ERROR;
     849          42 :   return guard([&] {
     850          21 :     auto accounts = array_of(account_indices, num_account_indices, "account_indices");
     851          51 :     wallet->wallet->tag_accounts(std::string(tag), accounts);
     852          38 :   });
     853             : }
     854             : 
     855          31 : monero_result monero_wallet_untag_accounts(::monero_wallet* wallet, const uint32_t* account_indices, size_t num_account_indices) {
     856          31 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
     857          44 :   return guard([&] {
     858          22 :     auto accounts = array_of(account_indices, num_account_indices, "account_indices");
     859          17 :     wallet->wallet->untag_accounts(accounts);
     860          39 :   });
     861             : }
     862             : 
     863         185 : monero_result monero_wallet_get_attribute(::monero_wallet* wallet, const char* key, bool* out_found, char** out_value) {
     864         185 :   reset_out(out_value);
     865         185 :   if (!require(wallet, "wallet") || !require(key, "key") || !require(out_found, "out_found") || !require(out_value, "out_value")) return MONERO_ERROR;
     866         328 :   return guard([&] {
     867         164 :     std::string value;
     868         186 :     *out_found = wallet->wallet->get_attribute(std::string(key), value);
     869         153 :     if (*out_found) *out_value = dup_string(value);
     870         328 :   });
     871             : }
     872             : 
     873         180 : monero_result monero_wallet_set_attribute(::monero_wallet* wallet, const char* key, const char* value) {
     874         180 :   if (!require(wallet, "wallet") || !require(key, "key")) return MONERO_ERROR;
     875         362 :   return guard([&] { wallet->wallet->set_attribute(std::string(key), safe_str(value)); });
     876             : }
     877             : 
     878          26 : monero_result monero_wallet_get_default_fee_priority(::monero_wallet* wallet, ::monero_tx_priority* out_priority) {
     879          26 :   if (!require(wallet, "wallet") || !require(out_priority, "out_priority")) return MONERO_ERROR;
     880          26 :   return guard([&] { *out_priority = static_cast<::monero_tx_priority>(wallet->wallet->get_default_fee_priority()); });
     881             : }
     882             : 
     883          29 : monero_result monero_wallet_get_path(::monero_wallet* wallet, char** out_path) {
     884          29 :   reset_out(out_path);
     885          29 :   if (!require(wallet, "wallet") || !require(out_path, "out_path")) return MONERO_ERROR;
     886          32 :   return guard([&] { *out_path = dup_string(wallet->wallet->get_path()); });
     887             : }
     888             : 
     889          26 : monero_result monero_wallet_get_public_view_key(::monero_wallet* wallet, char** out_key) {
     890          26 :   reset_out(out_key);
     891          26 :   if (!require(wallet, "wallet") || !require(out_key, "out_key")) return MONERO_ERROR;
     892          26 :   return guard([&] { *out_key = dup_string(wallet->wallet->get_public_view_key()); });
     893             : }
     894             : 
     895          26 : monero_result monero_wallet_get_public_spend_key(::monero_wallet* wallet, char** out_key) {
     896          26 :   reset_out(out_key);
     897          26 :   if (!require(wallet, "wallet") || !require(out_key, "out_key")) return MONERO_ERROR;
     898          26 :   return guard([&] { *out_key = dup_string(wallet->wallet->get_public_spend_key()); });
     899             : }
     900             : 
     901          26 : monero_result monero_wallet_get_version(::monero_wallet* wallet, char** out_json) {
     902          26 :   reset_out(out_json);
     903          26 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     904          26 :   return guard([&] { *out_json = dup_string(wallet->wallet->get_version().serialize()); });
     905             : }
     906             : 
     907          34 : monero_result monero_wallet_get_restore_height(::monero_wallet* wallet, uint64_t* out_height) {
     908          34 :   if (!require(wallet, "wallet") || !require(out_height, "out_height")) return MONERO_ERROR;
     909          42 :   return guard([&] { *out_height = wallet->wallet->get_restore_height(); });
     910             : }
     911             : 
     912          22 : monero_result monero_wallet_set_restore_height(::monero_wallet* wallet, uint64_t restore_height) {
     913          22 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
     914          26 :   return guard([&] { wallet->wallet->set_restore_height(restore_height); });
     915             : }
     916             : 
     917          28 : monero_result monero_wallet_is_closed(::monero_wallet* wallet, bool* out_closed) {
     918          28 :   if (!require(wallet, "wallet") || !require(out_closed, "out_closed")) return MONERO_ERROR;
     919          30 :   return guard([&] { *out_closed = wallet->wallet->is_closed(); });
     920             : }
     921             : 
     922          27 : monero_result monero_wallet_export_key_images(::monero_wallet* wallet, bool all, char** out_json) {
     923          27 :   reset_out(out_json);
     924          27 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     925          28 :   return guard([&] { *out_json = dup_string(wallet->wallet->export_key_images(all)->serialize()); });
     926             : }
     927             : 
     928         242 : monero_result monero_wallet_import_key_images(::monero_wallet* wallet, const char* key_images_json, uint64_t offset, char** out_json) {
     929         242 :   reset_out(out_json);
     930         242 :   if (!require(wallet, "wallet") || !require(key_images_json, "key_images_json") || !require(out_json, "out_json")) return MONERO_ERROR;
     931         976 :   return guard([&] { *out_json = dup_string(wallet->wallet->import_key_images(parse_key_images(key_images_json), offset)->serialize()); });
     932             : }
     933             : 
     934          27 : monero_result monero_wallet_export_outputs(::monero_wallet* wallet, bool all, char** out_hex) {
     935          27 :   reset_out(out_hex);
     936          27 :   if (!require(wallet, "wallet") || !require(out_hex, "out_hex")) return MONERO_ERROR;
     937          28 :   return guard([&] { *out_hex = dup_string(wallet->wallet->export_outputs(all)); });
     938             : }
     939             : 
     940         241 : monero_result monero_wallet_import_outputs(::monero_wallet* wallet, const char* outputs_hex, int32_t* out_num_imported) {
     941         241 :   if (!require(wallet, "wallet") || !require(outputs_hex, "outputs_hex") || !require(out_num_imported, "out_num_imported")) return MONERO_ERROR;
     942         894 :   return guard([&] { *out_num_imported = wallet->wallet->import_outputs(std::string(outputs_hex)); });
     943             : }
     944             : 
     945         176 : monero_result monero_wallet_freeze_output(::monero_wallet* wallet, const char* key_image) {
     946         176 :   if (!require(wallet, "wallet") || !require(key_image, "key_image")) return MONERO_ERROR;
     947         652 :   return guard([&] { wallet->wallet->freeze_output(std::string(key_image)); });
     948             : }
     949             : 
     950          26 : monero_result monero_wallet_thaw_output(::monero_wallet* wallet, const char* key_image) {
     951          26 :   if (!require(wallet, "wallet") || !require(key_image, "key_image")) return MONERO_ERROR;
     952          52 :   return guard([&] { wallet->wallet->thaw_output(std::string(key_image)); });
     953             : }
     954             : 
     955         180 : monero_result monero_wallet_is_output_frozen(::monero_wallet* wallet, const char* key_image, bool* out_frozen) {
     956         180 :   if (!require(wallet, "wallet") || !require(key_image, "key_image") || !require(out_frozen, "out_frozen")) return MONERO_ERROR;
     957         652 :   return guard([&] { *out_frozen = wallet->wallet->is_output_frozen(std::string(key_image)); });
     958             : }
     959             : 
     960         180 : monero_result monero_wallet_get_tx_note(::monero_wallet* wallet, const char* tx_hash, char** out_note) {
     961         180 :   reset_out(out_note);
     962         180 :   if (!require(wallet, "wallet") || !require(tx_hash, "tx_hash") || !require(out_note, "out_note")) return MONERO_ERROR;
     963         612 :   return guard([&] { *out_note = dup_string(wallet->wallet->get_tx_note(std::string(tx_hash))); });
     964             : }
     965             : 
     966         180 : monero_result monero_wallet_set_tx_note(::monero_wallet* wallet, const char* tx_hash, const char* note) {
     967         180 :   if (!require(wallet, "wallet") || !require(tx_hash, "tx_hash")) return MONERO_ERROR;
     968         628 :   return guard([&] { wallet->wallet->set_tx_note(std::string(tx_hash), safe_str(note)); });
     969             : }
     970             : 
     971         191 : monero_result monero_wallet_get_tx_notes(::monero_wallet* wallet, const char* const* tx_hashes, size_t num_tx_hashes, char** out_json) {
     972         191 :   reset_out(out_json);
     973         191 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     974         500 :   return guard([&] { *out_json = dup_string(json_of_strings(wallet->wallet->get_tx_notes(string_array(tx_hashes, num_tx_hashes, "tx_hashes")))); });
     975             : }
     976             : 
     977          44 : monero_result monero_wallet_set_tx_notes(::monero_wallet* wallet, const char* const* tx_hashes, const char* const* notes, size_t num_tx_hashes) {
     978          44 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
     979          84 :   return guard([&] { wallet->wallet->set_tx_notes(string_array(tx_hashes, num_tx_hashes, "tx_hashes"), string_array(notes, num_tx_hashes, "notes")); });
     980             : }
     981             : 
     982          49 : monero_result monero_wallet_set_daemon_connection(::monero_wallet* wallet, const char* uri, const char* username, const char* password, const char* proxy_uri, const bool* is_trusted, bool ssl_verify) {
     983          49 :   if (!require(wallet, "wallet") || !require(uri, "uri")) return MONERO_ERROR;
     984          72 :   return guard([&] {
     985             :     // the connection object carries ssl_verify, the uri overload of monero-cpp doesn't take it
     986          76 :     std::shared_ptr<monero::monero_rpc_connection> connection = std::make_shared<monero::monero_rpc_connection>(std::string(uri), safe_str(username), safe_str(password), safe_str(proxy_uri));
     987          28 :     connection->m_ssl_verify = ssl_verify;
     988          28 :     wallet->wallet->set_daemon_connection(connection, optional_of(is_trusted));
     989          64 :   });
     990             : }
     991             : 
     992          28 : monero_result monero_wallet_get_daemon_connection(::monero_wallet* wallet, char** out_json) {
     993          28 :   reset_out(out_json);
     994          28 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
     995          30 :   return guard([&] {
     996          15 :     std::shared_ptr<monero::monero_rpc_connection> connection = wallet->wallet->get_daemon_connection();
     997           7 :     if (connection) *out_json = dup_string(connection->serialize());
     998          22 :   });
     999             : }
    1000             : 
    1001          32 : monero_result monero_wallet_is_daemon_trusted(::monero_wallet* wallet, bool* out_trusted) {
    1002          32 :   if (!require(wallet, "wallet") || !require(out_trusted, "out_trusted")) return MONERO_ERROR;
    1003          38 :   return guard([&] { *out_trusted = wallet->wallet->is_daemon_trusted(); });
    1004             : }
    1005             : 
    1006             : // -------------------------------- LISTENERS ---------------------------------
    1007             : 
    1008          18 : monero_result monero_wallet_listener_create(const monero_wallet_listener_callbacks* callbacks, ::monero_wallet_listener** out_listener) {
    1009          18 :   reset_out(out_listener);
    1010          18 :   if (!require(callbacks, "callbacks") || !require(out_listener, "out_listener")) return MONERO_ERROR;
    1011          26 :   return guard([&] {
    1012          13 :     std::unique_ptr<::monero_wallet_listener> listener(new ::monero_wallet_listener());
    1013          13 :     listener->adapter.owner = listener.get();
    1014          13 :     listener->adapter.callbacks = *callbacks;
    1015          13 :     *out_listener = listener.release();
    1016          26 :   });
    1017             : }
    1018             : 
    1019          16 : void monero_wallet_listener_free(::monero_wallet_listener* listener) {
    1020          16 :   if (listener == nullptr) return;
    1021          13 :   std::shared_ptr<monero::monero_wallet> wallet;
    1022             :   {
    1023          13 :     std::lock_guard<std::mutex> lock(listener->registration_mutex);
    1024          13 :     wallet = listener->registered_with.lock();
    1025          13 :   }
    1026          13 :   if (wallet) {
    1027             :     try {
    1028           3 :       wallet->remove_listener(listener->adapter);
    1029           0 :     } catch (...) {
    1030             :       // release the listener even if the removal fails
    1031           0 :     }
    1032             :   }
    1033          13 :   delete listener;
    1034          13 : }
    1035             : 
    1036          90 : monero_result monero_wallet_add_listener(::monero_wallet* wallet, ::monero_wallet_listener* listener) {
    1037          90 :   if (!require(wallet, "wallet") || !require(listener, "listener")) return MONERO_ERROR;
    1038         154 :   return guard([&] {
    1039             :     // reserve the wallet under the lock, so one listener can't join two wallets
    1040          77 :     std::shared_ptr<monero::monero_wallet> previous;
    1041             :     {
    1042          77 :       std::lock_guard<std::mutex> lock(listener->registration_mutex);
    1043          77 :       previous = listener->registered_with.lock();
    1044          77 :       if (previous && previous != wallet->wallet) throw std::runtime_error("listener is already registered with another wallet");
    1045          50 :       listener->registered_with = wallet->wallet;
    1046          77 :     }
    1047             :     try {
    1048          50 :       wallet->wallet->add_listener(listener->adapter);
    1049          10 :     } catch (...) {
    1050          10 :       if (!previous) {
    1051          10 :         std::lock_guard<std::mutex> lock(listener->registration_mutex);
    1052          10 :         if (listener->registered_with.lock() == wallet->wallet) listener->registered_with.reset();
    1053          10 :       }
    1054          10 :       throw;
    1055          10 :     }
    1056         154 :   });
    1057             : }
    1058             : 
    1059          30 : monero_result monero_wallet_remove_listener(::monero_wallet* wallet, ::monero_wallet_listener* listener) {
    1060          30 :   if (!require(wallet, "wallet") || !require(listener, "listener")) return MONERO_ERROR;
    1061          34 :   return guard([&] {
    1062             :     // monero-cpp notifies under its own lock, so this runs without registration_mutex
    1063          17 :     wallet->wallet->remove_listener(listener->adapter);
    1064          14 :     std::lock_guard<std::mutex> lock(listener->registration_mutex);
    1065          14 :     if (listener->registered_with.lock() == wallet->wallet) listener->registered_with.reset();
    1066          31 :   });
    1067             : }
    1068             : 
    1069          72 : monero_result monero_wallet_remove_listeners(::monero_wallet* wallet) {
    1070          72 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
    1071         126 :   return guard([&] {
    1072          89 :     for (monero::monero_wallet_listener* listener : wallet->wallet->get_listeners()) {
    1073          26 :       wallet->wallet->remove_listener(*listener);
    1074             :       // listeners that don't come from the C ABI have no handle to reset
    1075          26 :       auto* adapter = dynamic_cast<wallet_listener_adapter*>(listener);
    1076          26 :       if (adapter == nullptr) continue;
    1077          26 :       std::lock_guard<std::mutex> lock(adapter->owner->registration_mutex);
    1078          26 :       if (adapter->owner->registered_with.lock() == wallet->wallet) adapter->owner->registered_with.reset();
    1079          81 :     }
    1080         118 :   });
    1081             : }
    1082             : 
    1083             : // -------------------------------- DAEMON ------------------------------------
    1084             : 
    1085          44 : monero_result monero_wallet_sync(::monero_wallet* wallet, const uint64_t* start_height, ::monero_wallet_listener* listener, char** out_json) {
    1086          44 :   reset_out(out_json);
    1087          44 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1088          62 :   return guard([&] {
    1089          31 :     monero::monero_sync_result result = listener ? sync_with_listener(wallet, listener, start_height) : (start_height ? wallet->wallet->sync(*start_height) : wallet->wallet->sync());
    1090           8 :     *out_json = dup_string(result.serialize());
    1091          39 :   });
    1092             : }
    1093             : 
    1094          25 : monero_result monero_wallet_start_syncing(::monero_wallet* wallet, uint64_t sync_period_ms) {
    1095          25 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
    1096          32 :   return guard([&] {
    1097          16 :     check_period_ms(sync_period_ms);
    1098           9 :     wallet->wallet->start_syncing(sync_period_ms);
    1099          16 :   });
    1100             : }
    1101             : 
    1102          22 : monero_result monero_wallet_stop_syncing(::monero_wallet* wallet) {
    1103          22 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
    1104          26 :   return guard([&] { wallet->wallet->stop_syncing(); });
    1105             : }
    1106             : 
    1107          30 : monero_result monero_wallet_is_connected_to_daemon(::monero_wallet* wallet, bool* out_connected) {
    1108          30 :   if (!require(wallet, "wallet") || !require(out_connected, "out_connected")) return MONERO_ERROR;
    1109          34 :   return guard([&] { *out_connected = wallet->wallet->is_connected_to_daemon(); });
    1110             : }
    1111             : 
    1112          26 : monero_result monero_wallet_is_daemon_synced(::monero_wallet* wallet, bool* out_synced) {
    1113          26 :   if (!require(wallet, "wallet") || !require(out_synced, "out_synced")) return MONERO_ERROR;
    1114          26 :   return guard([&] { *out_synced = wallet->wallet->is_daemon_synced(); });
    1115             : }
    1116             : 
    1117          26 : monero_result monero_wallet_is_synced(::monero_wallet* wallet, bool* out_synced) {
    1118          26 :   if (!require(wallet, "wallet") || !require(out_synced, "out_synced")) return MONERO_ERROR;
    1119          26 :   return guard([&] { *out_synced = wallet->wallet->is_synced(); });
    1120             : }
    1121             : 
    1122          26 : monero_result monero_wallet_get_daemon_height(::monero_wallet* wallet, uint64_t* out_height) {
    1123          26 :   if (!require(wallet, "wallet") || !require(out_height, "out_height")) return MONERO_ERROR;
    1124          26 :   return guard([&] { *out_height = wallet->wallet->get_daemon_height(); });
    1125             : }
    1126             : 
    1127          26 : monero_result monero_wallet_get_daemon_max_peer_height(::monero_wallet* wallet, uint64_t* out_height) {
    1128          26 :   if (!require(wallet, "wallet") || !require(out_height, "out_height")) return MONERO_ERROR;
    1129          26 :   return guard([&] { *out_height = wallet->wallet->get_daemon_max_peer_height(); });
    1130             : }
    1131             : 
    1132          14 : monero_result monero_wallet_wait_for_next_block(::monero_wallet* wallet, uint64_t* out_height) {
    1133          14 :   if (!require(wallet, "wallet") || !require(out_height, "out_height")) return MONERO_ERROR;
    1134           2 :   return guard([&] { *out_height = wallet->wallet->wait_for_next_block(); });
    1135             : }
    1136             : 
    1137          35 : monero_result monero_wallet_scan_txs(::monero_wallet* wallet, const char* const* tx_hashes, size_t num_tx_hashes) {
    1138          35 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
    1139          52 :   return guard([&] { wallet->wallet->scan_txs(string_array(tx_hashes, num_tx_hashes, "tx_hashes")); });
    1140             : }
    1141             : 
    1142          22 : monero_result monero_wallet_rescan_blockchain(::monero_wallet* wallet) {
    1143          22 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
    1144          26 :   return guard([&] { wallet->wallet->rescan_blockchain(); });
    1145             : }
    1146             : 
    1147          22 : monero_result monero_wallet_rescan_spent(::monero_wallet* wallet) {
    1148          22 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
    1149          26 :   return guard([&] { wallet->wallet->rescan_spent(); });
    1150             : }
    1151             : 
    1152          26 : monero_result monero_wallet_get_height_by_date(::monero_wallet* wallet, uint16_t year, uint8_t month, uint8_t day, uint64_t* out_height) {
    1153          26 :   if (!require(wallet, "wallet") || !require(out_height, "out_height")) return MONERO_ERROR;
    1154          26 :   return guard([&] { *out_height = wallet->wallet->get_height_by_date(year, month, day); });
    1155             : }
    1156             : 
    1157          34 : monero_result monero_wallet_start_mining(::monero_wallet* wallet, const uint64_t* num_threads, const bool* background_mining, const bool* ignore_battery) {
    1158          34 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
    1159          50 :   return guard([&] { wallet->wallet->start_mining(optional_of(num_threads), optional_of(background_mining), optional_of(ignore_battery)); });
    1160             : }
    1161             : 
    1162          22 : monero_result monero_wallet_stop_mining(::monero_wallet* wallet) {
    1163          22 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
    1164          26 :   return guard([&] { wallet->wallet->stop_mining(); });
    1165             : }
    1166             : 
    1167             : // ------------------------------ TRANSACTIONS ---------------------------------
    1168             : // the models of monero-cpp point to their parents with shared_ptr (a tx to its block and its tx set, an output or a transfer to its tx),
    1169             : // so a result is only freed after monero_utils::free() resets those pointers. The calls that return models free them once their JSON
    1170             : // is built, with a scope handler, so that it runs when the call throws too
    1171             : 
    1172         568 : monero_result monero_wallet_get_txs(::monero_wallet* wallet, const char* query_json, char** out_json) {
    1173         568 :   reset_out(out_json);
    1174         568 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1175        1110 :   return guard([&] {
    1176             :     // a query with a transfer, input or output query points to them and they point back. monero_utils::free() resets the
    1177             :     // transfer and the output query, so the input query is reset here
    1178         555 :     std::shared_ptr<monero::monero_tx_query> query;
    1179         555 :     if (query_json) query = tx_query_of(query_json);
    1180         536 :     auto free_query = epee::misc_utils::create_scope_leave_handler([&] { release_tx_query(query); });
    1181         268 :     auto txs = query ? wallet->wallet->get_txs(*query) : wallet->wallet->get_txs();
    1182          38 :     auto blocks = monero_utils::get_blocks_from_txs(txs);
    1183          76 :     auto free_blocks = epee::misc_utils::create_scope_leave_handler([&] { monero_utils::free(blocks); });
    1184          38 :     *out_json = dup_string(json_of_blocks(blocks));
    1185        1340 :   });
    1186             : }
    1187             : 
    1188         220 : monero_result monero_wallet_get_transfers(::monero_wallet* wallet, const char* query_json, char** out_json) {
    1189         220 :   reset_out(out_json);
    1190         220 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1191         414 :   return guard([&] {
    1192         207 :     std::shared_ptr<monero::monero_transfer_query> query = transfer_query_of(query_json);
    1193         100 :     auto free_query = epee::misc_utils::create_scope_leave_handler([&] { release_tx_query(query->m_tx_query); });
    1194          50 :     auto transfers = wallet->wallet->get_transfers(*query);
    1195          35 :     auto blocks = monero_utils::get_blocks_from_transfers(transfers);
    1196          70 :     auto free_blocks = epee::misc_utils::create_scope_leave_handler([&] { monero_utils::free(blocks); });
    1197          35 :     *out_json = dup_string(json_of_blocks(blocks));
    1198         272 :   });
    1199             : }
    1200             : 
    1201         220 : monero_result monero_wallet_get_outputs(::monero_wallet* wallet, const char* query_json, char** out_json) {
    1202         220 :   reset_out(out_json);
    1203         220 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1204         414 :   return guard([&] {
    1205         207 :     std::shared_ptr<monero::monero_output_query> query = output_query_of(query_json);
    1206         110 :     auto free_query = epee::misc_utils::create_scope_leave_handler([&] { release_tx_query(query->m_tx_query); });
    1207          55 :     auto outputs = wallet->wallet->get_outputs(*query);
    1208          45 :     auto blocks = blocks_of_outputs(outputs);
    1209          90 :     auto free_blocks = epee::misc_utils::create_scope_leave_handler([&] { monero_utils::free(blocks); });
    1210          45 :     *out_json = dup_string(json_of_blocks(blocks));
    1211         272 :   });
    1212             : }
    1213             : 
    1214         183 : monero_result monero_wallet_create_tx(::monero_wallet* wallet, const char* config_json, char** out_json) {
    1215         183 :   reset_out(out_json);
    1216         183 :   if (!require(wallet, "wallet") || !require(config_json, "config_json") || !require(out_json, "out_json")) return MONERO_ERROR;
    1217         332 :   return guard([&] {
    1218         208 :     auto tx = wallet->wallet->create_tx(*model_json<monero::monero_tx_config>(config_json, "config"));
    1219           4 :     auto free_tx = epee::misc_utils::create_scope_leave_handler([&] { monero_utils::free(tx); });
    1220           4 :     *out_json = dup_string(json_of_tx_sets(tx_sets_of({tx})));
    1221         170 :   });
    1222             : }
    1223             : 
    1224         181 : monero_result monero_wallet_create_txs(::monero_wallet* wallet, const char* config_json, char** out_json) {
    1225         181 :   reset_out(out_json);
    1226         181 :   if (!require(wallet, "wallet") || !require(config_json, "config_json") || !require(out_json, "out_json")) return MONERO_ERROR;
    1227         328 :   return guard([&] {
    1228             :     // the transactions share one tx set, which holds the unsigned hex that sign_txs() takes
    1229         214 :     auto txs = wallet->wallet->create_txs(*model_json<monero::monero_tx_config>(config_json, "config"));
    1230           2 :     auto free_txs = epee::misc_utils::create_scope_leave_handler([&] { monero_utils::free(txs); });
    1231           1 :     if (txs.empty()) throw std::runtime_error("no transactions were created");
    1232           1 :     *out_json = dup_string(json_of_tx_sets(tx_sets_of(txs)));
    1233         165 :   });
    1234             : }
    1235             : 
    1236         240 : monero_result monero_wallet_relay_tx(::monero_wallet* wallet, const char* tx_metadata, char** out_hash) {
    1237         240 :   reset_out(out_hash);
    1238         240 :   if (!require(wallet, "wallet") || !require(tx_metadata, "tx_metadata") || !require(out_hash, "out_hash")) return MONERO_ERROR;
    1239         892 :   return guard([&] { *out_hash = dup_string(wallet->wallet->relay_tx(std::string(tx_metadata))); });
    1240             : }
    1241             : 
    1242         189 : monero_result monero_wallet_relay_tx_json(::monero_wallet* wallet, const char* tx_json, char** out_hash) {
    1243         189 :   reset_out(out_hash);
    1244         189 :   if (!require(wallet, "wallet") || !require(tx_json, "tx_json") || !require(out_hash, "out_hash")) return MONERO_ERROR;
    1245         741 :   return guard([&] { *out_hash = dup_string(wallet->wallet->relay_tx(tx_metadata_of_json(tx_json))); });
    1246             : }
    1247             : 
    1248          40 : monero_result monero_wallet_relay_txs(::monero_wallet* wallet, const char* const* tx_metadatas, size_t num_tx_metadatas, char** out_json) {
    1249          40 :   reset_out(out_json);
    1250          40 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1251          71 :   return guard([&] { *out_json = dup_string(json_of_strings(wallet->wallet->relay_txs(string_array(tx_metadatas, num_tx_metadatas, "tx_metadatas")))); });
    1252             : }
    1253             : 
    1254         184 : monero_result monero_wallet_relay_txs_json(::monero_wallet* wallet, const char* txs_json, char** out_json) {
    1255         184 :   reset_out(out_json);
    1256         184 :   if (!require(wallet, "wallet") || !require(txs_json, "txs_json") || !require(out_json, "out_json")) return MONERO_ERROR;
    1257         709 :   return guard([&] { *out_json = dup_string(json_of_strings(wallet->wallet->relay_txs(tx_metadatas_of_json(txs_json)))); });
    1258             : }
    1259             : 
    1260         240 : monero_result monero_wallet_submit_txs(::monero_wallet* wallet, const char* signed_tx_hex, char** out_json) {
    1261         240 :   reset_out(out_json);
    1262         240 :   if (!require(wallet, "wallet") || !require(signed_tx_hex, "signed_tx_hex") || !require(out_json, "out_json")) return MONERO_ERROR;
    1263         892 :   return guard([&] { *out_json = dup_string(json_of_strings(wallet->wallet->submit_txs(std::string(signed_tx_hex)))); });
    1264             : }
    1265             : 
    1266         242 : monero_result monero_wallet_sign_txs(::monero_wallet* wallet, const char* unsigned_tx_hex, char** out_json) {
    1267         242 :   reset_out(out_json);
    1268         242 :   if (!require(wallet, "wallet") || !require(unsigned_tx_hex, "unsigned_tx_hex") || !require(out_json, "out_json")) return MONERO_ERROR;
    1269         450 :   return guard([&] {
    1270             :     // the txs of a signed set have no block, set or transfers, so nothing points back at them and there is nothing to release
    1271         673 :     monero::monero_tx_set tx_set = wallet->wallet->sign_txs(std::string(unsigned_tx_hex));
    1272           1 :     *out_json = dup_string("{\"txSets\":[" + tx_set.serialize() + "]}");
    1273         226 :   });
    1274             : }
    1275             : 
    1276         252 : monero_result monero_wallet_describe_tx_set(::monero_wallet* wallet, const char* tx_set_json, char** out_json) {
    1277         252 :   reset_out(out_json);
    1278         252 :   if (!require(wallet, "wallet") || !require(tx_set_json, "tx_set_json") || !require(out_json, "out_json")) return MONERO_ERROR;
    1279         470 :   return guard([&] {
    1280         345 :     monero::monero_tx_set described = wallet->wallet->describe_tx_set(*tx_set_of_hex(tx_set_json));
    1281           2 :     auto free_described = epee::misc_utils::create_scope_leave_handler([&] { monero_utils::free(described.m_txs); });
    1282           1 :     *out_json = dup_string("{\"txSets\":[" + described.serialize() + "]}");
    1283         236 :   });
    1284             : }
    1285             : 
    1286         182 : monero_result monero_wallet_sweep_output(::monero_wallet* wallet, const char* config_json, char** out_json) {
    1287         182 :   reset_out(out_json);
    1288         182 :   if (!require(wallet, "wallet") || !require(config_json, "config_json") || !require(out_json, "out_json")) return MONERO_ERROR;
    1289         330 :   return guard([&] {
    1290         223 :     auto tx = wallet->wallet->sweep_output(*model_json<monero::monero_tx_config>(config_json, "config"));
    1291           2 :     auto free_tx = epee::misc_utils::create_scope_leave_handler([&] { monero_utils::free(tx); });
    1292           2 :     *out_json = dup_string(json_of_tx_sets(tx_sets_of({tx})));
    1293         167 :   });
    1294             : }
    1295             : 
    1296          26 : monero_result monero_wallet_sweep_dust(::monero_wallet* wallet, bool relay, char** out_json) {
    1297          26 :   reset_out(out_json);
    1298          26 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1299          26 :   return guard([&] {
    1300          13 :     auto txs = wallet->wallet->sweep_dust(relay);
    1301           2 :     auto free_txs = epee::misc_utils::create_scope_leave_handler([&] { monero_utils::free(txs); });
    1302           1 :     *out_json = dup_string(json_of_tx_sets(tx_sets_of(txs)));
    1303          14 :   });
    1304             : }
    1305             : 
    1306         181 : monero_result monero_wallet_sweep_unlocked(::monero_wallet* wallet, const char* config_json, char** out_json) {
    1307         181 :   reset_out(out_json);
    1308         181 :   if (!require(wallet, "wallet") || !require(config_json, "config_json") || !require(out_json, "out_json")) return MONERO_ERROR;
    1309         328 :   return guard([&] {
    1310         206 :     auto txs = wallet->wallet->sweep_unlocked(*model_json<monero::monero_tx_config>(config_json, "config"));
    1311           6 :     auto free_txs = epee::misc_utils::create_scope_leave_handler([&] { monero_utils::free(txs); });
    1312           3 :     *out_json = dup_string(json_of_tx_sets(tx_sets_of(txs)));
    1313         167 :   });
    1314             : }
    1315             : 
    1316          31 : monero_result monero_wallet_move_to(::monero_wallet* wallet, const char* path, const char* password) {
    1317          31 :   if (!require(wallet, "wallet") || !require(path, "path")) return MONERO_ERROR;
    1318          66 :   return guard([&] { wallet->wallet->move_to(std::string(path), safe_str(password)); });
    1319             : }
    1320             : 
    1321         181 : monero_result monero_wallet_get_payment_uri(::monero_wallet* wallet, const char* config_json, char** out_uri) {
    1322         181 :   reset_out(out_uri);
    1323         181 :   if (!require(wallet, "wallet") || !require(config_json, "config_json") || !require(out_uri, "out_uri")) return MONERO_ERROR;
    1324         373 :   return guard([&] { *out_uri = dup_string(wallet->wallet->get_payment_uri(*model_json<monero::monero_tx_config>(config_json, "config"))); });
    1325             : }
    1326             : 
    1327         180 : monero_result monero_wallet_parse_payment_uri(::monero_wallet* wallet, const char* uri, char** out_json) {
    1328         180 :   reset_out(out_json);
    1329         180 :   if (!require(wallet, "wallet") || !require(uri, "uri") || !require(out_json, "out_json")) return MONERO_ERROR;
    1330         590 :   return guard([&] { *out_json = dup_string(wallet->wallet->parse_payment_uri(std::string(uri))->serialize()); });
    1331             : }
    1332             : 
    1333         241 : monero_result monero_wallet_get_tx_key(::monero_wallet* wallet, const char* tx_hash, char** out_key) {
    1334         241 :   reset_out(out_key);
    1335         241 :   if (!require(wallet, "wallet") || !require(tx_hash, "tx_hash") || !require(out_key, "out_key")) return MONERO_ERROR;
    1336         894 :   return guard([&] { *out_key = dup_string(wallet->wallet->get_tx_key(std::string(tx_hash))); });
    1337             : }
    1338             : 
    1339         188 : monero_result monero_wallet_check_tx_key(::monero_wallet* wallet, const char* tx_hash, const char* tx_key, const char* address, char** out_json) {
    1340         188 :   reset_out(out_json);
    1341         188 :   if (!require(wallet, "wallet") || !require(tx_hash, "tx_hash") || !require(tx_key, "tx_key") || !require(address, "address") || !require(out_json, "out_json")) return MONERO_ERROR;
    1342        1304 :   return guard([&] { *out_json = dup_string(wallet->wallet->check_tx_key(std::string(tx_hash), std::string(tx_key), std::string(address))->serialize()); });
    1343             : }
    1344             : 
    1345          39 : monero_result monero_wallet_get_tx_proof(::monero_wallet* wallet, const char* tx_hash, const char* address, const char* message, char** out_signature) {
    1346          39 :   reset_out(out_signature);
    1347          39 :   if (!require(wallet, "wallet") || !require(tx_hash, "tx_hash") || !require(address, "address") || !require(out_signature, "out_signature")) return MONERO_ERROR;
    1348         121 :   return guard([&] { *out_signature = dup_string(wallet->wallet->get_tx_proof(std::string(tx_hash), std::string(address), safe_str(message))); });
    1349             : }
    1350             : 
    1351         193 : monero_result monero_wallet_check_tx_proof(::monero_wallet* wallet, const char* tx_hash, const char* address, const char* message, const char* signature, char** out_json) {
    1352         193 :   reset_out(out_json);
    1353         193 :   if (!require(wallet, "wallet") || !require(tx_hash, "tx_hash") || !require(address, "address") || !require(signature, "signature") || !require(out_json, "out_json")) return MONERO_ERROR;
    1354        1498 :   return guard([&] { *out_json = dup_string(wallet->wallet->check_tx_proof(std::string(tx_hash), std::string(address), safe_str(message), std::string(signature))->serialize()); });
    1355             : }
    1356             : 
    1357          35 : monero_result monero_wallet_get_spend_proof(::monero_wallet* wallet, const char* tx_hash, const char* message, char** out_signature) {
    1358          35 :   reset_out(out_signature);
    1359          35 :   if (!require(wallet, "wallet") || !require(tx_hash, "tx_hash") || !require(out_signature, "out_signature")) return MONERO_ERROR;
    1360          87 :   return guard([&] { *out_signature = dup_string(wallet->wallet->get_spend_proof(std::string(tx_hash), safe_str(message))); });
    1361             : }
    1362             : 
    1363         189 : monero_result monero_wallet_check_spend_proof(::monero_wallet* wallet, const char* tx_hash, const char* message, const char* signature, bool* out_good) {
    1364         189 :   if (!require(wallet, "wallet") || !require(tx_hash, "tx_hash") || !require(signature, "signature") || !require(out_good, "out_good")) return MONERO_ERROR;
    1365        1166 :   return guard([&] { *out_good = wallet->wallet->check_spend_proof(std::string(tx_hash), safe_str(message), std::string(signature)); });
    1366             : }
    1367             : 
    1368          31 : monero_result monero_wallet_get_reserve_proof_wallet(::monero_wallet* wallet, const char* message, char** out_signature) {
    1369          31 :   reset_out(out_signature);
    1370          31 :   if (!require(wallet, "wallet") || !require(out_signature, "out_signature")) return MONERO_ERROR;
    1371          52 :   return guard([&] { *out_signature = dup_string(wallet->wallet->get_reserve_proof_wallet(safe_str(message))); });
    1372             : }
    1373             : 
    1374          30 : monero_result monero_wallet_get_reserve_proof_account(::monero_wallet* wallet, uint32_t account_idx, uint64_t amount, const char* message, char** out_signature) {
    1375          30 :   reset_out(out_signature);
    1376          30 :   if (!require(wallet, "wallet") || !require(out_signature, "out_signature")) return MONERO_ERROR;
    1377          50 :   return guard([&] { *out_signature = dup_string(wallet->wallet->get_reserve_proof_account(account_idx, amount, safe_str(message))); });
    1378             : }
    1379             : 
    1380         249 : monero_result monero_wallet_check_reserve_proof(::monero_wallet* wallet, const char* address, const char* message, const char* signature, char** out_json) {
    1381         249 :   reset_out(out_json);
    1382         249 :   if (!require(wallet, "wallet") || !require(address, "address") || !require(signature, "signature") || !require(out_json, "out_json")) return MONERO_ERROR;
    1383        1566 :   return guard([&] { *out_json = dup_string(wallet->wallet->check_reserve_proof(std::string(address), safe_str(message), std::string(signature))->serialize()); });
    1384             : }
    1385             : 
    1386             : // -------------------------------- MULTISIG ----------------------------------
    1387             : 
    1388          27 : monero_result monero_wallet_is_multisig_import_needed(::monero_wallet* wallet, bool* out_needed) {
    1389          27 :   if (!require(wallet, "wallet") || !require(out_needed, "out_needed")) return MONERO_ERROR;
    1390          28 :   return guard([&] { *out_needed = wallet->wallet->is_multisig_import_needed(); });
    1391             : }
    1392             : 
    1393          26 : monero_result monero_wallet_get_multisig_info(::monero_wallet* wallet, char** out_json) {
    1394          26 :   reset_out(out_json);
    1395          26 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1396          26 :   return guard([&] { *out_json = dup_string(wallet->wallet->get_multisig_info().serialize()); });
    1397             : }
    1398             : 
    1399          29 : monero_result monero_wallet_prepare_multisig(::monero_wallet* wallet, char** out_multisig_hex) {
    1400          29 :   reset_out(out_multisig_hex);
    1401          29 :   if (!require(wallet, "wallet") || !require(out_multisig_hex, "out_multisig_hex")) return MONERO_ERROR;
    1402          32 :   return guard([&] { *out_multisig_hex = dup_string(wallet->wallet->prepare_multisig()); });
    1403             : }
    1404             : 
    1405         254 : monero_result monero_wallet_make_multisig(::monero_wallet* wallet, const char* const* multisig_hexes, size_t num_multisig_hexes, int32_t threshold, const char* password, char** out_multisig_hex) {
    1406         254 :   reset_out(out_multisig_hex);
    1407         254 :   if (!require(wallet, "wallet") || !require(out_multisig_hex, "out_multisig_hex")) return MONERO_ERROR;
    1408         950 :   return guard([&] { *out_multisig_hex = dup_string(wallet->wallet->make_multisig(string_array(multisig_hexes, num_multisig_hexes, "multisig_hexes"), threshold, safe_str(password))); });
    1409             : }
    1410             : 
    1411         257 : monero_result monero_wallet_exchange_multisig_keys(::monero_wallet* wallet, const char* const* multisig_hexes, size_t num_multisig_hexes, const char* password, char** out_json) {
    1412         257 :   reset_out(out_json);
    1413         257 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1414         956 :   return guard([&] { *out_json = dup_string(wallet->wallet->exchange_multisig_keys(string_array(multisig_hexes, num_multisig_hexes, "multisig_hexes"), safe_str(password)).serialize()); });
    1415             : }
    1416             : 
    1417          29 : monero_result monero_wallet_export_multisig_hex(::monero_wallet* wallet, char** out_multisig_hex) {
    1418          29 :   reset_out(out_multisig_hex);
    1419          29 :   if (!require(wallet, "wallet") || !require(out_multisig_hex, "out_multisig_hex")) return MONERO_ERROR;
    1420          32 :   return guard([&] { *out_multisig_hex = dup_string(wallet->wallet->export_multisig_hex()); });
    1421             : }
    1422             : 
    1423         248 : monero_result monero_wallet_import_multisig_hex(::monero_wallet* wallet, const char* const* multisig_hexes, size_t num_multisig_hexes, bool refresh_after_import, int32_t* out_num_imported) {
    1424         248 :   if (!require(wallet, "wallet") || !require(out_num_imported, "out_num_imported")) return MONERO_ERROR;
    1425         470 :   return guard([&] { *out_num_imported = wallet->wallet->import_multisig_hex(string_array(multisig_hexes, num_multisig_hexes, "multisig_hexes"), refresh_after_import); });
    1426             : }
    1427             : 
    1428         240 : monero_result monero_wallet_sign_multisig_tx_hex(::monero_wallet* wallet, const char* multisig_tx_hex, char** out_json) {
    1429         240 :   reset_out(out_json);
    1430         240 :   if (!require(wallet, "wallet") || !require(multisig_tx_hex, "multisig_tx_hex") || !require(out_json, "out_json")) return MONERO_ERROR;
    1431         892 :   return guard([&] { *out_json = dup_string(wallet->wallet->sign_multisig_tx_hex(std::string(multisig_tx_hex)).serialize()); });
    1432             : }
    1433             : 
    1434         240 : monero_result monero_wallet_submit_multisig_tx_hex(::monero_wallet* wallet, const char* signed_multisig_tx_hex, char** out_json) {
    1435         240 :   reset_out(out_json);
    1436         240 :   if (!require(wallet, "wallet") || !require(signed_multisig_tx_hex, "signed_multisig_tx_hex") || !require(out_json, "out_json")) return MONERO_ERROR;
    1437         892 :   return guard([&] { *out_json = dup_string(json_of_strings(wallet->wallet->submit_multisig_tx_hex(std::string(signed_multisig_tx_hex)))); });
    1438             : }
    1439             : 
    1440             : // -------------------------------- RPC WALLETS -------------------------------
    1441             : 
    1442          10 : monero_result monero_wallet_rpc_connect(::monero_rpc_connection* connection, ::monero_wallet** out_wallet) {
    1443          10 :   reset_out(out_wallet);
    1444          10 :   if (!require(connection, "connection") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
    1445          12 :   return guard([&] { *out_wallet = wrap_shared(std::make_shared<monero::monero_wallet_rpc>(monero_c::connection_of(connection))); });
    1446             : }
    1447             : 
    1448          31 : monero_result monero_wallet_rpc_open_wallet(::monero_wallet* wallet, const char* name, const char* wallet_password) {
    1449          31 :   if (!require(wallet, "wallet") || !require(name, "name")) return MONERO_ERROR;
    1450          44 :   return guard([&] { as_rpc(wallet).open_wallet(std::string(name), safe_str(wallet_password)); });
    1451             : }
    1452             : 
    1453         177 : monero_result monero_wallet_rpc_create_wallet(::monero_wallet* wallet, const char* config_json) {
    1454         177 :   if (!require(wallet, "wallet") || !require(config_json, "config_json")) return MONERO_ERROR;
    1455         328 :   return guard([&] { as_rpc(wallet).create_wallet(model_json<monero::monero_wallet_config>(config_json, "config")); });
    1456             : }
    1457             : 
    1458          11 : monero_result monero_wallet_rpc_open(const char* uri, const char* username, const char* password, const char* name, const char* wallet_password, ::monero_wallet** out_wallet) {
    1459          11 :   reset_out(out_wallet);
    1460          11 :   if (!require(uri, "uri") || !require(name, "name") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
    1461          12 :   return guard([&] {
    1462           6 :     std::shared_ptr<monero::monero_wallet_rpc> rpc = rpc_client(uri, username, password);
    1463          16 :     rpc->open_wallet(std::string(name), safe_str(wallet_password));
    1464           0 :     *out_wallet = wrap_shared(rpc);
    1465          10 :   });
    1466             : }
    1467             : 
    1468          12 : monero_result monero_wallet_rpc_create_random(const char* uri, const char* username, const char* password, const char* name, const char* wallet_password, const char* language, ::monero_wallet** out_wallet) {
    1469          12 :   reset_out(out_wallet);
    1470          12 :   if (!require(uri, "uri") || !require(name, "name") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
    1471          14 :   return guard([&] { *out_wallet = create_rpc_wallet(uri, username, password, rpc_config(name, wallet_password, language)); });
    1472             : }
    1473             : 
    1474          14 : monero_result monero_wallet_rpc_create_from_seed(const char* uri, const char* username, const char* password, const char* name, const char* wallet_password, const char* seed, const char* seed_offset, uint64_t restore_height, const char* language, ::monero_wallet** out_wallet) {
    1475          14 :   reset_out(out_wallet);
    1476          14 :   if (!require(uri, "uri") || !require(name, "name") || !require(seed, "seed") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
    1477          18 :   return guard([&] {
    1478           9 :     auto config = rpc_config(name, wallet_password, language);
    1479           9 :     config->m_seed = mnemonic_of(seed);
    1480           7 :     config->m_seed_offset = safe_str(seed_offset);
    1481           7 :     config->m_restore_height = restore_height;
    1482           7 :     *out_wallet = create_rpc_wallet(uri, username, password, config);
    1483          18 :   });
    1484             : }
    1485             : 
    1486          15 : monero_result monero_wallet_rpc_create_from_keys(const char* uri, const char* username, const char* password, const char* name, const char* wallet_password, const char* address, const char* private_view_key, const char* private_spend_key, uint64_t restore_height, const char* language, ::monero_wallet** out_wallet) {
    1487          15 :   reset_out(out_wallet);
    1488          15 :   if (!require(uri, "uri") || !require(name, "name") || !require(address, "address") || !require(private_view_key, "private_view_key") || !require(out_wallet, "out_wallet")) return MONERO_ERROR;
    1489          16 :   return guard([&] {
    1490           8 :     auto config = rpc_config(name, wallet_password, language);
    1491           8 :     config->m_primary_address = std::string(address);
    1492           8 :     config->m_private_view_key = std::string(private_view_key);
    1493           8 :     config->m_private_spend_key = safe_str(private_spend_key);
    1494           8 :     config->m_restore_height = restore_height;
    1495           8 :     *out_wallet = create_rpc_wallet(uri, username, password, config);
    1496          16 :   });
    1497             : }
    1498             : 
    1499          22 : monero_result monero_wallet_rpc_stop(::monero_wallet* wallet) {
    1500          22 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
    1501          26 :   return guard([&] { as_rpc(wallet).stop(); });
    1502             : }
    1503             : 
    1504          25 : monero_result monero_wallet_rpc_get_seed_languages(::monero_wallet* wallet, char** out_json) {
    1505          25 :   reset_out(out_json);
    1506          25 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1507          24 :   return guard([&] { *out_json = dup_string(json_of_strings(as_rpc(wallet).get_seed_languages())); });
    1508             : }
    1509             : 
    1510          27 : monero_result monero_wallet_rpc_get_connection(::monero_wallet* wallet, char** out_json) {
    1511          27 :   reset_out(out_json);
    1512          27 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1513          28 :   return guard([&] { *out_json = dup_string(as_rpc(wallet).get_rpc_connection()->serialize()); });
    1514             : }
    1515             : 
    1516          24 : monero_result monero_wallet_rpc_set_poll_period(::monero_wallet* wallet, uint64_t period_ms) {
    1517          24 :   if (!require(wallet, "wallet")) return MONERO_ERROR;
    1518          30 :   return guard([&] {
    1519          15 :     check_period_ms(period_ms);
    1520           9 :     as_rpc(wallet).set_poll_period_in_ms(period_ms);
    1521          15 :   });
    1522             : }
    1523             : 
    1524          33 : monero_result monero_wallet_rpc_get_balances(::monero_wallet* wallet, const uint32_t* account_idx, const uint32_t* subaddress_idx, char** out_json) {
    1525          33 :   reset_out(out_json);
    1526          33 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1527          40 :   return guard([&] {
    1528          20 :     std::shared_ptr<monero::monero_subaddress> balances = as_rpc(wallet).get_balances(optional_of(account_idx), optional_of(subaddress_idx));
    1529           0 :     *out_json = dup_string(balances ? balances->serialize() : std::string("null"));
    1530          20 :   });
    1531             : }
    1532             : 
    1533          25 : monero_result monero_wallet_rpc_get_account(::monero_wallet* wallet, uint32_t account_idx, bool include_subaddresses, bool skip_balances, char** out_json) {
    1534          25 :   reset_out(out_json);
    1535          25 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1536          24 :   return guard([&] { *out_json = dup_string(as_rpc(wallet).get_account(account_idx, include_subaddresses, skip_balances).serialize()); });
    1537             : }
    1538             : 
    1539          29 : monero_result monero_wallet_rpc_get_accounts(::monero_wallet* wallet, bool include_subaddresses, const char* tag, bool skip_balances, char** out_json) {
    1540          29 :   reset_out(out_json);
    1541          29 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1542          36 :   return guard([&] { *out_json = dup_string(json_of_list(as_rpc(wallet).get_accounts(include_subaddresses, safe_str(tag), skip_balances))); });
    1543             : }
    1544             : 
    1545          34 : monero_result monero_wallet_rpc_get_subaddresses(::monero_wallet* wallet, uint32_t account_idx, const uint32_t* subaddress_indices, size_t num_subaddress_indices, bool skip_balances, char** out_json) {
    1546          34 :   reset_out(out_json);
    1547          34 :   if (!require(wallet, "wallet") || !require(out_json, "out_json")) return MONERO_ERROR;
    1548          42 :   return guard([&] {
    1549          21 :     auto indices = array_of(subaddress_indices, num_subaddress_indices, "subaddress_indices");
    1550          16 :     *out_json = dup_string(json_of_list(as_rpc(wallet).get_subaddresses(account_idx, indices, skip_balances)));
    1551          37 :   });
    1552             : }
    1553             : 
    1554         629 : monero_result monero_wallet_rpc_set_daemon_connection(::monero_wallet* wallet, const char* connection_json, bool is_trusted, const char* ssl_options_json) {
    1555         629 :   if (!require(wallet, "wallet") || !require(connection_json, "connection_json")) return MONERO_ERROR;
    1556        1232 :   return guard([&] {
    1557         616 :     boost::optional<monero::ssl_options> options;
    1558         616 :     if (ssl_options_json != nullptr && ssl_options_json[0] != '\0') options = *model_json<monero::ssl_options>(ssl_options_json, "ssl_options");
    1559         534 :     as_rpc(wallet).set_daemon_connection(model_json<monero::monero_rpc_connection>(connection_json, "connection"), is_trusted, options);
    1560        1232 :   });
    1561             : }
    1562             : 
    1563             : } // extern "C"

Generated by: LCOV version 1.16