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"
|