Electroneum
Loading...
Searching...
No Matches
wallet_rpc_server.cpp
Go to the documentation of this file.
1// Copyrights(c) 2017-2021, The Electroneum Project
2// Copyrights(c) 2014-2019, The Monero Project
3//
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without modification, are
7// permitted provided that the following conditions are met:
8//
9// 1. Redistributions of source code must retain the above copyright notice, this list of
10// conditions and the following disclaimer.
11//
12// 2. Redistributions in binary form must reproduce the above copyright notice, this list
13// of conditions and the following disclaimer in the documentation and/or other
14// materials provided with the distribution.
15//
16// 3. Neither the name of the copyright holder nor the names of its contributors may be
17// used to endorse or promote products derived from this software without specific
18// prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//
30// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
31#include <boost/format.hpp>
32#include <boost/asio/ip/address.hpp>
33#include <boost/filesystem/operations.hpp>
34#include <boost/algorithm/string.hpp>
35#include <boost/preprocessor/stringize.hpp>
36#include <cstdint>
37#include "include_base_utils.h"
38using namespace epee;
39
40#include "wallet_rpc_server.h"
41#include "wallet/wallet_args.h"
42#include "common/command_line.h"
43#include "common/i18n.h"
44#include "cryptonote_config.h"
47#include "multisig/multisig.h"
49#include "misc_language.h"
50#include "string_coding.h"
51#include "string_tools.h"
52#include "crypto/hash.h"
54#include "rpc/rpc_args.h"
57
58#undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
59#define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.rpc"
60
61#define DEFAULT_AUTO_REFRESH_PERIOD 20 // seconds
62
63namespace
64{
65 const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
66 const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"};
67 const command_line::arg_descriptor<bool> arg_restricted = {"restricted-rpc", "Restricts to view-only commands", false};
68 const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
69 const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false};
70 const command_line::arg_descriptor<std::string> arg_data_dir = {"data-dir", "Blockchain database path."};
71 const command_line::arg_descriptor<bool> arg_testnet = {"testnet", "For testnet. Daemon must also be launched with --testnet flag"};
72
73 constexpr const char default_rpc_username[] = "electroneum";
74
75 boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify)
76 {
77 auto pwd_container = tools::password_container::prompt(verify, prompt);
78 if (!pwd_container)
79 {
80 MERROR("failed to read wallet password");
81 }
82 return pwd_container;
83 }
84 //------------------------------------------------------------------------------------------------------------------------------
85 void set_confirmations(tools::wallet_rpc::transfer_entry &entry, uint64_t blockchain_height, uint64_t block_reward)
86 {
87 if (entry.height >= blockchain_height || (entry.height == 0 && (!strcmp(entry.type.c_str(), "pending") || !strcmp(entry.type.c_str(), "pool"))))
88 entry.confirmations = 0;
89 else
90 entry.confirmations = blockchain_height - entry.height;
91
92 if (block_reward == 0)
94 else if (entry.type == "migration" || entry.type == "sc-migration")
96 else
97 entry.suggested_confirmations_threshold = (entry.amount + block_reward - 1) / block_reward;
98 }
99}
100
101namespace tools
102{
103 const char* wallet_rpc_server::tr(const char* str)
104 {
105 return i18n_translate(str, "tools::wallet_rpc_server");
106 }
107
108 //------------------------------------------------------------------------------------------------------------------------------
109 wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_restricted(false), m_vm(NULL)
110 {
111 }
112 //------------------------------------------------------------------------------------------------------------------------------
114 {
115 if (m_wallet)
116 delete m_wallet;
117 }
118 //------------------------------------------------------------------------------------------------------------------------------
120 {
121 m_wallet = cr;
122 }
123 //------------------------------------------------------------------------------------------------------------------------------
125 {
126 m_stop = false;
127 m_net_server.add_idle_handler([this](){
128 if (m_auto_refresh_period == 0) // disabled
129 return true;
130 if (boost::posix_time::microsec_clock::universal_time() < m_last_auto_refresh_time + boost::posix_time::seconds(m_auto_refresh_period))
131 return true;
132 try {
133 if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon());
134 } catch (const std::exception& ex) {
135 LOG_ERROR("Exception at while refreshing, what=" << ex.what());
136 }
137 m_last_auto_refresh_time = boost::posix_time::microsec_clock::universal_time();
138 return true;
139 }, 1000);
140 m_net_server.add_idle_handler([this](){
141 if (m_stop.load(std::memory_order_relaxed))
142 {
144 return false;
145 }
146 return true;
147 }, 500);
148
149 //DO NOT START THIS SERVER IN MORE THEN 1 THREADS WITHOUT REFACTORING
151 }
152 //------------------------------------------------------------------------------------------------------------------------------
154 {
155 if (m_wallet)
156 {
157 m_wallet->store();
158 delete m_wallet;
159 m_wallet = NULL;
160 }
161 }
162 //------------------------------------------------------------------------------------------------------------------------------
163 bool wallet_rpc_server::init(const boost::program_options::variables_map *vm)
164 {
165 auto rpc_config = cryptonote::rpc_args::process(*vm);
166 if (!rpc_config)
167 return false;
168
169 m_vm = vm;
170
171 auto data_dir = command_line::get_arg(*m_vm, arg_data_dir);
172 m_testnet = command_line::get_arg(*m_vm, arg_testnet);
173
174 boost::optional<epee::net_utils::http::login> http_login{};
175 std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port);
176 const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login);
177 m_restricted = command_line::get_arg(*m_vm, arg_restricted);
178 if (!command_line::is_arg_defaulted(*m_vm, arg_wallet_dir))
179 {
181 {
182 MERROR(arg_wallet_dir.name << " and " << wallet_args::arg_wallet_file().name << " are incompatible, use only one of them");
183 return false;
184 }
185 m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir);
186#ifdef _WIN32
187#define MKDIR(path, mode) mkdir(path)
188#else
189#define MKDIR(path, mode) mkdir(path, mode)
190#endif
191 if (!m_wallet_dir.empty() && MKDIR(m_wallet_dir.c_str(), 0700) < 0 && errno != EEXIST)
192 {
193#ifdef _WIN32
194 LOG_ERROR(tr("Failed to create directory ") + m_wallet_dir);
195#else
196 LOG_ERROR((boost::format(tr("Failed to create directory %s: %s")) % m_wallet_dir % strerror(errno)).str());
197#endif
198 return false;
199 }
200 }
201
202 if (disable_auth)
203 {
204 if (rpc_config->login)
205 {
207 LOG_ERROR(tr("Cannot specify --") << arg_disable_rpc_login.name << tr(" and --") << arg.rpc_login.name);
208 return false;
209 }
210 }
211 else // auth enabled
212 {
213 if (!rpc_config->login)
214 {
215 std::array<std::uint8_t, 16> rand_128bit{{}};
216 crypto::rand(rand_128bit.size(), rand_128bit.data());
217 http_login.emplace(
218 default_rpc_username,
219 string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size())
220 );
221
222 std::string temp = "electroneum-wallet-rpc." + bind_port + ".login";
223 rpc_login_file = tools::private_file::create(temp);
224 if (!rpc_login_file.handle())
225 {
226 LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file"));
227 return false;
228 }
229 std::fputs(http_login->username.c_str(), rpc_login_file.handle());
230 std::fputc(':', rpc_login_file.handle());
231 const epee::wipeable_string password = http_login->password;
232 std::fwrite(password.data(), 1, password.size(), rpc_login_file.handle());
233 std::fflush(rpc_login_file.handle());
234 if (std::ferror(rpc_login_file.handle()))
235 {
236 LOG_ERROR(tr("Error writing to file ") << temp);
237 return false;
238 }
239 LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp);
240 }
241 else // chosen user/pass
242 {
243 http_login.emplace(
244 std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()
245 );
246 }
247 assert(bool(http_login));
248 } // end auth enabled
249
250 m_auto_refresh_period = DEFAULT_AUTO_REFRESH_PERIOD;
251 m_last_auto_refresh_time = boost::posix_time::min_date_time;
252
253 check_background_mining();
254
255 m_net_server.set_threads_prefix("RPC");
256 auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
258 rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login),
259 std::move(rpc_config->ssl_options)
260 );
261 }
262 //------------------------------------------------------------------------------------------------------------------------------
263 void wallet_rpc_server::check_background_mining()
264 {
265 if (!m_wallet)
266 return;
267
270 {
271 //MLOG_RED(el::Level::Warning, "Background mining not enabled. Run \"set setup-background-mining 1\" in electroneum-wallet-cli to change.");
272 return;
273 }
274
275 if (!m_wallet->is_trusted_daemon())
276 {
277 MDEBUG("Using an untrusted daemon, skipping background mining check");
278 return;
279 }
280
283 bool r = m_wallet->invoke_http_json("/mining_status", req, res);
284 if (!r || res.status != CORE_RPC_STATUS_OK)
285 {
286 MERROR("Failed to query mining status: " << (r ? res.status : "No connection to daemon"));
287 return;
288 }
289 if (res.active || res.is_background_mining_enabled)
290 return;
291
293 {
294 //MINFO("The daemon is not set up to background mine.");
295 //MINFO("With background mining enabled, the daemon will mine when idle and not on batttery.");
296 //MINFO("Enabling this supports the network you are using, and makes you eligible for receiving new electroneum");
297 //MINFO("Set setup-background-mining to 1 in electroneum-wallet-cli to change.");
298 return;
299 }
300
303 req2.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
304 req2.threads_count = 1;
305 req2.do_background_mining = true;
306 req2.ignore_battery = false;
307 r = m_wallet->invoke_http_json("/start_mining", req2, res);
308 if (!r || res2.status != CORE_RPC_STATUS_OK)
309 {
310 //MERROR("Failed to setup background mining: " << (r ? res.status : "No connection to daemon"));
311 return;
312 }
313
314 //MINFO("Background mining enabled. The daemon will mine when idle and not on batttery.");
315 }
316 //------------------------------------------------------------------------------------------------------------------------------
317 bool wallet_rpc_server::not_open(epee::json_rpc::error& er)
318 {
320 er.message = "No wallet file";
321 return false;
322 }
323 //------------------------------------------------------------------------------------------------------------------------------
324 void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd)
325 {
327 entry.payment_id = string_tools::pod_to_hex(payment_id);
328 if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
329 entry.payment_id = entry.payment_id.substr(0,16);
330 entry.height = pd.m_block_height;
331 entry.timestamp = pd.m_timestamp;
332 entry.amount = pd.m_amount;
333 entry.unlock_time = pd.m_unlock_time;
334 entry.fee = pd.m_fee;
335 entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
336 entry.type = pd.m_coinbase ? "block" : "in";
338 entry.subaddr_indices.push_back(pd.m_subaddr_index);
339 entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
340 set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
341 }
342 //------------------------------------------------------------------------------------------------------------------------------
343 void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd)
344 {
345 entry.txid = string_tools::pod_to_hex(txid);
347 if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
348 entry.payment_id = entry.payment_id.substr(0,16);
349 entry.height = pd.m_block_height;
350 entry.timestamp = pd.m_timestamp;
351 entry.unlock_time = pd.m_unlock_time;
352 entry.fee = pd.m_amount_in - pd.m_amount_out;
353 uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
354 entry.amount = pd.m_is_migration ? pd.m_amount_out : pd.m_amount_in - change - entry.fee;
355 entry.note = m_wallet->get_tx_note(txid);
356
357
358 for (const auto &d: pd.m_dests) {
359 entry.destinations.push_back(wallet_rpc::transfer_destination());
360 wallet_rpc::transfer_destination &td = entry.destinations.back();
361 td.amount = d.amount;
362 td.address = d.original.empty() ? get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) : d.original;
363 }
364
365
366 entry.type = pd.m_is_migration ? "migration" : pd.m_is_sc_migration ? "sc-migration" : "out";
367 entry.subaddr_index = { pd.m_subaddr_account, 0 };
368 for (uint32_t i: pd.m_subaddr_indices)
369 entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
370 entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
371 set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
372 }
373 //------------------------------------------------------------------------------------------------------------------------------
374 void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd)
375 {
377 entry.txid = string_tools::pod_to_hex(txid);
380 if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
381 entry.payment_id = entry.payment_id.substr(0,16);
382 entry.height = 0;
383 entry.timestamp = pd.m_timestamp;
384 entry.fee = pd.m_amount_in - pd.m_amount_out;
385 entry.amount = pd.m_amount_in - pd.m_change - entry.fee;
386 entry.unlock_time = pd.m_tx.unlock_time;
387 entry.note = m_wallet->get_tx_note(txid);
388
389 for (const auto &d: pd.m_dests) {
390 entry.destinations.push_back(wallet_rpc::transfer_destination());
391 wallet_rpc::transfer_destination &td = entry.destinations.back();
392 td.amount = d.amount;
393 td.address = d.original.empty() ? get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) : d.original;
394 }
395
396 entry.type = is_failed ? "failed" : "pending";
397 entry.subaddr_index = { pd.m_subaddr_account, 0 };
398 for (uint32_t i: pd.m_subaddr_indices)
399 entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
400 entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
401 set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
402 }
403 //------------------------------------------------------------------------------------------------------------------------------
404 void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd)
405 {
406 const tools::wallet2::payment_details &pd = ppd.m_pd;
408 entry.payment_id = string_tools::pod_to_hex(payment_id);
409 if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos)
410 entry.payment_id = entry.payment_id.substr(0,16);
411 entry.height = 0;
412 entry.timestamp = pd.m_timestamp;
413 entry.amount = pd.m_amount;
414 entry.unlock_time = pd.m_unlock_time;
415 entry.fee = pd.m_fee;
416 entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
419 entry.type = "pool";
421 entry.subaddr_indices.push_back(pd.m_subaddr_index);
422 entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
423 set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward());
424 }
425 //------------------------------------------------------------------------------------------------------------------------------
426 bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
427 {
428 if (!m_wallet) return not_open(er);
429 try
430 {
431 bool syncedV10 = m_wallet->synced_to_v10();
432 res.balance = req.all_accounts ? m_wallet->balance_all(syncedV10) : m_wallet->balance(req.account_index, syncedV10);
433 res.unlocked_balance = req.all_accounts ? m_wallet->unlocked_balance_all(syncedV10, &res.blocks_to_unlock) : m_wallet->unlocked_balance(req.account_index, syncedV10, &res.blocks_to_unlock);
434 res.multisig_import_needed = m_wallet->multisig() && m_wallet->has_multisig_partial_key_images();
435 std::map<uint32_t, std::map<uint32_t, uint64_t>> balance_per_subaddress_per_account;
436 std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account;
437 if (req.all_accounts)
438 {
439 for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index)
440 {
441 balance_per_subaddress_per_account[account_index] = m_wallet->balance_per_subaddress(account_index, syncedV10);
442 unlocked_balance_per_subaddress_per_account[account_index] = m_wallet->unlocked_balance_per_subaddress(account_index, syncedV10);
443 }
444 }
445 else
446 {
447 balance_per_subaddress_per_account[req.account_index] = m_wallet->balance_per_subaddress(req.account_index, syncedV10);
448 unlocked_balance_per_subaddress_per_account[req.account_index] = m_wallet->unlocked_balance_per_subaddress(req.account_index, syncedV10);
449 }
450 std::vector<tools::wallet2::transfer_details> transfers;
451 m_wallet->get_transfers(transfers);
452 for (const auto& p : balance_per_subaddress_per_account)
453 {
454 uint32_t account_index = p.first;
455 std::map<uint32_t, uint64_t> balance_per_subaddress = p.second;
456 std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = unlocked_balance_per_subaddress_per_account[account_index];
457 std::set<uint32_t> address_indices;
458 if (!req.all_accounts && !req.address_indices.empty())
459 {
460 address_indices = req.address_indices;
461 }
462 else
463 {
464 for (const auto& i : balance_per_subaddress)
465 address_indices.insert(i.first);
466 }
467 for (uint32_t i : address_indices)
468 {
469 wallet_rpc::COMMAND_RPC_GET_BALANCE::per_subaddress_info info;
470 info.account_index = account_index;
471 info.address_index = i;
472 cryptonote::subaddress_index index = {info.account_index, info.address_index};
473 info.address = m_wallet->get_subaddress_as_str(index);
474 info.balance = balance_per_subaddress[i];
475 info.unlocked_balance = unlocked_balance_per_subaddress[i].first;
476 info.blocks_to_unlock = unlocked_balance_per_subaddress[i].second;
477 info.label = m_wallet->get_subaddress_label(index);
478 info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index && (syncedV10 ? td.m_tx.version > 1 : true); });
479 res.per_subaddress.emplace_back(std::move(info));
480 }
481 }
482 }
483 catch (const std::exception& e)
484 {
485 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
486 return false;
487 }
488 return true;
489 }
490 //------------------------------------------------------------------------------------------------------------------------------
491 bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
492 {
493 if (!m_wallet) return not_open(er);
494 try
495 {
496 THROW_WALLET_EXCEPTION_IF(req.account_index >= m_wallet->get_num_subaddress_accounts(), error::account_index_outofbound);
497 res.addresses.clear();
498 std::vector<uint32_t> req_address_index;
499 if (req.address_index.empty())
500 {
501 for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(req.account_index); ++i)
502 req_address_index.push_back(i);
503 }
504 else
505 {
506 req_address_index = req.address_index;
507 }
509 m_wallet->get_transfers(transfers);
510 for (uint32_t i : req_address_index)
511 {
512 THROW_WALLET_EXCEPTION_IF(i >= m_wallet->get_num_subaddresses(req.account_index), error::address_index_outofbound);
513 res.addresses.resize(res.addresses.size() + 1);
514 auto& info = res.addresses.back();
515 const cryptonote::subaddress_index index = {req.account_index, i};
516 info.address = m_wallet->get_subaddress_as_str(index);
517 info.label = m_wallet->get_subaddress_label(index);
518 info.address_index = index.minor;
519 info.used = std::find_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return td.m_subaddr_index == index; }) != transfers.end();
520 }
521 res.address = m_wallet->get_subaddress_as_str({req.account_index, 0});
522 }
523 catch (const std::exception& e)
524 {
525 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
526 return false;
527 }
528 return true;
529 }
530 //------------------------------------------------------------------------------------------------------------------------------
531 bool wallet_rpc_server::on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er, const connection_context *ctx)
532 {
533 if (!m_wallet) return not_open(er);
534 cryptonote::address_parse_info info;
535 if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
536 {
538 er.message = "Invalid address";
539 return false;
540 }
541 auto index = m_wallet->get_subaddress_index(info.address);
542 if (!index)
543 {
545 er.message = "Address doesn't belong to the wallet";
546 return false;
547 }
548 res.index = *index;
549 return true;
550 }
551 //------------------------------------------------------------------------------------------------------------------------------
552 bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
553 {
554 if (!m_wallet) return not_open(er);
555 try
556 {
557 m_wallet->add_subaddress(req.account_index, req.label);
558 res.address_index = m_wallet->get_num_subaddresses(req.account_index) - 1;
559 res.address = m_wallet->get_subaddress_as_str({req.account_index, res.address_index});
560 }
561 catch (const std::exception& e)
562 {
563 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
564 return false;
565 }
566 return true;
567 }
568 //------------------------------------------------------------------------------------------------------------------------------
569 bool wallet_rpc_server::on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
570 {
571 if (!m_wallet) return not_open(er);
572 try
573 {
574 m_wallet->set_subaddress_label(req.index, req.label);
575 }
576 catch (const std::exception& e)
577 {
578 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
579 return false;
580 }
581 return true;
582 }
583 //------------------------------------------------------------------------------------------------------------------------------
584 bool wallet_rpc_server::on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
585 {
586 if (!m_wallet) return not_open(er);
587 bool syncedV10 = m_wallet->synced_to_v10();
588 try
589 {
590 res.total_balance = 0;
591 res.total_unlocked_balance = 0;
592 cryptonote::subaddress_index subaddr_index = {0,0};
593
594 uint64_t acc_major_offset = m_wallet->account_major_offset();
595
596 // Filter by Account Index for performance
597 if(!req.account_index.empty()) {
598 subaddr_index.major = static_cast<uint32_t>(std::stoul(req.account_index));
599
600 wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
601 info.account_index = subaddr_index.major;
602 info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
603 info.balance = m_wallet->balance(subaddr_index.major, syncedV10);
604 info.unlocked_balance = m_wallet->unlocked_balance(subaddr_index.major, syncedV10);
605 info.label = m_wallet->get_subaddress_label(subaddr_index);
606 res.subaddress_accounts.push_back(info);
607 res.total_balance += info.balance;
608 res.total_unlocked_balance += info.unlocked_balance;
609 res.account_major_offset = acc_major_offset;
610
611 return true;
612 }
613
614 const std::pair<std::map<std::string, std::string>, std::vector<std::string>> account_tags = m_wallet->get_account_tags();
615 if (!req.tag.empty() && account_tags.first.count(req.tag) == 0)
616 {
618 er.message = (boost::format(tr("Tag %s is unregistered.")) % req.tag).str();
619 return false;
620 }
621 for (; subaddr_index.major < m_wallet->get_num_subaddress_accounts(); ++subaddr_index.major)
622 {
623 if (!req.tag.empty() && req.tag != account_tags.second[subaddr_index.major])
624 continue;
625 wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
626 info.account_index = subaddr_index.major;
627 info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
628 info.balance = m_wallet->balance(subaddr_index.major, syncedV10);
629 info.unlocked_balance = m_wallet->unlocked_balance(subaddr_index.major, syncedV10);
630 info.label = m_wallet->get_subaddress_label(subaddr_index);
631 info.tag = account_tags.second[subaddr_index.major];
632 res.subaddress_accounts.push_back(info);
633 res.total_balance += info.balance;
634 res.total_unlocked_balance += info.unlocked_balance;
635 res.account_major_offset = acc_major_offset;
636 }
637 }
638 catch (const std::exception& e)
639 {
640 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
641 return false;
642 }
643 return true;
644 }
645 //------------------------------------------------------------------------------------------------------------------------------
646 bool wallet_rpc_server::on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
647 {
648 if (!m_wallet) return not_open(er);
649 try
650 {
651 m_wallet->add_subaddress_account(req.label);
652 res.account_index = m_wallet->get_num_subaddress_accounts() - 1;
653 res.address = m_wallet->get_subaddress_as_str({res.account_index, 0});
654 }
655 catch (const std::exception& e)
656 {
657 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
658 return false;
659 }
660 return true;
661 }
662 //------------------------------------------------------------------------------------------------------------------------------
663 bool wallet_rpc_server::on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
664 {
665 if (!m_wallet) return not_open(er);
666 try
667 {
668 m_wallet->set_subaddress_label({req.account_index, 0}, req.label);
669 }
670 catch (const std::exception& e)
671 {
672 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
673 return false;
674 }
675 return true;
676 }
677 //------------------------------------------------------------------------------------------------------------------------------
678 bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
679 {
680 if (!m_wallet) return not_open(er);
681 const std::pair<std::map<std::string, std::string>, std::vector<std::string>> account_tags = m_wallet->get_account_tags();
682 for (const std::pair<std::string, std::string>& p : account_tags.first)
683 {
684 res.account_tags.resize(res.account_tags.size() + 1);
685 auto& info = res.account_tags.back();
686 info.tag = p.first;
687 info.label = p.second;
688 for (size_t i = 0; i < account_tags.second.size(); ++i)
689 {
690 if (account_tags.second[i] == info.tag)
691 info.accounts.push_back(i);
692 }
693 }
694 return true;
695 }
696 //------------------------------------------------------------------------------------------------------------------------------
697 bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
698 {
699 if (!m_wallet) return not_open(er);
700 try
701 {
702 m_wallet->set_account_tag(req.accounts, req.tag);
703 }
704 catch (const std::exception& e)
705 {
706 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
707 return false;
708 }
709 return true;
710 }
711 //------------------------------------------------------------------------------------------------------------------------------
712 bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
713 {
714 if (!m_wallet) return not_open(er);
715 try
716 {
717 m_wallet->set_account_tag(req.accounts, "");
718 }
719 catch (const std::exception& e)
720 {
721 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
722 return false;
723 }
724 return true;
725 }
726 //------------------------------------------------------------------------------------------------------------------------------
727 bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er, const connection_context *ctx)
728 {
729 if (!m_wallet) return not_open(er);
730 try
731 {
732 m_wallet->set_account_tag_description(req.tag, req.description);
733 }
734 catch (const std::exception& e)
735 {
736 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
737 return false;
738 }
739 return true;
740 }
741 //------------------------------------------------------------------------------------------------------------------------------
742 bool wallet_rpc_server::on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
743 {
744 if (!m_wallet) return not_open(er);
745 try
746 {
747 res.height = m_wallet->get_blockchain_current_height();
748 }
749 catch (const std::exception& e)
750 {
751 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
752 return false;
753 }
754 return true;
755 }
756 //------------------------------------------------------------------------------------------------------------------------------
757 bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination, epee::json_rpc::error& er)
758 {
759 crypto::hash8 integrated_payment_id = crypto::null_hash8;
760 std::string extra_nonce;
761 for (auto it = destinations.begin(); it != destinations.end(); it++)
762 {
763 cryptonote::address_parse_info info;
764 cryptonote::tx_destination_entry de;
765 er.message = "";
766 if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), it->address,
767 [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
768 if (!dnssec_valid)
769 {
770 er.message = std::string("Invalid DNSSEC for ") + url;
771 return {};
772 }
773 if (addresses.empty())
774 {
775 er.message = std::string("No Electroneum address found at ") + url;
776 return {};
777 }
778 return addresses[0];
779 }))
780 {
782 if (er.message.empty())
783 er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address;
784 return false;
785 }
786
787 de.original = it->address;
788 de.addr = info.address;
789 de.is_subaddress = info.is_subaddress;
790 de.amount = it->amount;
791 de.is_integrated = info.has_payment_id;
792 dsts.push_back(de);
793
794 if (info.has_payment_id)
795 {
796 if (!payment_id.empty() || integrated_payment_id != crypto::null_hash8)
797 {
799 er.message = "A single payment id is allowed per transaction";
800 return false;
801 }
802
803 crypto::hash payment_id = crypto::null_hash;
804 memcpy(payment_id.data, info.payment_id.data, 8); // convert short pid to regular
805 memset(payment_id.data + 8, 0, 24); // merely a sanity check
806
807 cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
808
809 /* Append Payment ID data into extra */
810 if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
812 er.message = "Something went wrong with integrated payment_id.";
813 return false;
814 }
815 }
816 }
817
818 if (at_least_one_destination && dsts.empty())
819 {
821 er.message = "No destinations for this transfer";
822 return false;
823 }
824
825 if (!payment_id.empty())
826 {
827
828 /* Just to clarify */
829 const std::string& payment_id_str = payment_id;
830
831 crypto::hash long_payment_id;
832 crypto::hash8 short_payment_id;
833
834 /* Parse payment ID */
835 if (wallet2::parse_long_payment_id(payment_id_str, long_payment_id)) {
836 cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, long_payment_id);
837 }
838 else {
840 er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 64 character string";
841 return false;
842 }
843
844 /* Append Payment ID data into extra */
845 if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
847 er.message = "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string";
848 return false;
849 }
850
851 }
852 return true;
853 }
854 //------------------------------------------------------------------------------------------------------------------------------
855 static std::string ptx_to_string(const tools::wallet2::pending_tx &ptx)
856 {
857 std::ostringstream oss;
859 try
860 {
861 ar << ptx;
862 }
863 catch (...)
864 {
865 return "";
866 }
868 }
869 //------------------------------------------------------------------------------------------------------------------------------
870 template<typename T> static bool is_error_value(const T &val) { return false; }
871 static bool is_error_value(const std::string &s) { return s.empty(); }
872 //------------------------------------------------------------------------------------------------------------------------------
873 template<typename T, typename V>
874 static bool fill(T &where, V s)
875 {
876 if (is_error_value(s)) return false;
877 where = std::move(s);
878 return true;
879 }
880 //------------------------------------------------------------------------------------------------------------------------------
881 template<typename T, typename V>
882 static bool fill(std::list<T> &where, V s)
883 {
884 if (is_error_value(s)) return false;
885 where.emplace_back(std::move(s));
886 return true;
887 }
888 //------------------------------------------------------------------------------------------------------------------------------
889 static uint64_t total_amount(const tools::wallet2::pending_tx &ptx)
890 {
891 uint64_t amount = 0;
892 for (const auto &dest: ptx.dests) amount += dest.amount;
893 return amount;
894 }
895 //------------------------------------------------------------------------------------------------------------------------------
896 template<typename Ts, typename Tu>
897 bool wallet_rpc_server::fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
898 bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay,
899 Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er)
900 {
901 for (const auto & ptx : ptx_vector)
902 {
903 if (get_tx_key)
904 {
906 for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
907 s += epee::to_hex::wipeable_string(additional_tx_key);
908 fill(tx_key, std::string(s.data(), s.size()));
909 }
910 // Compute amount leaving wallet in tx. By convention dests does not include change outputs
911 fill(amount, total_amount(ptx));
912 fill(fee, ptx.fee);
913 }
914
915 if (m_wallet->multisig())
916 {
917 multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
918 if (multisig_txset.empty())
919 {
921 er.message = "Failed to save multisig tx set after creation";
922 return false;
923 }
924 }
925 else
926 {
927 if (m_wallet->watch_only()){
928 unsigned_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->dump_tx_to_str(ptx_vector));
929 if (unsigned_txset.empty())
930 {
932 er.message = "Failed to save unsigned tx set after creation";
933 return false;
934 }
935 }
936 else if (!do_not_relay)
937 m_wallet->commit_tx(ptx_vector);
938
939 // populate response with tx hashes
940 for (auto & ptx : ptx_vector)
941 {
943 r = r && (!get_tx_hex || fill(tx_blob, epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx))));
944 r = r && (!get_tx_metadata || fill(tx_metadata, ptx_to_string(ptx)));
945 if (!r)
946 {
948 er.message = "Failed to save tx info";
949 return false;
950 }
951 }
952 }
953 return true;
954 }
955 //------------------------------------------------------------------------------------------------------------------------------
956 bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
957 {
958
959 std::vector<cryptonote::tx_destination_entry> dsts;
960 std::vector<uint8_t> extra;
961
962 LOG_PRINT_L3("on_transfer starts");
963 if (!m_wallet) return not_open(er);
964 if (m_restricted)
965 {
967 er.message = "Command unavailable in restricted mode.";
968 return false;
969 }
970
971 // validate the transfer requested and populate dsts & extra
972 if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, true, er))
973 {
974 return false;
975 }
976
977 try
978 {
979 uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
980 uint32_t priority = m_wallet->adjust_priority(req.priority);
981 std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
982
983 if (ptx_vector.empty())
984 {
986 er.message = "No transaction created";
987 return false;
988 }
989
990 // reject proposed transactions if there are more than one. see on_transfer_split below.
991 if (ptx_vector.size() != 1)
992 {
994 er.message = "Transaction would be too large. try /transfer_split.";
995 return false;
996 }
997
998 return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
999 res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
1000 }
1001 catch (const std::exception& e)
1002 {
1003 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR);
1004 return false;
1005 }
1006 return true;
1007 }
1008 //------------------------------------------------------------------------------------------------------------------------------
1009 bool wallet_rpc_server::on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1010 {
1011
1012 std::vector<cryptonote::tx_destination_entry> dsts;
1013 std::vector<uint8_t> extra;
1014
1015 if (!m_wallet) return not_open(er);
1016 if (m_restricted)
1017 {
1019 er.message = "Command unavailable in restricted mode.";
1020 return false;
1021 }
1022
1023 // validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types.
1024 if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, true, er))
1025 {
1026 return false;
1027 }
1028
1029 try
1030 {
1031 uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
1032 uint32_t priority = m_wallet->adjust_priority(req.priority);
1033 LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
1034 std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
1035 LOG_PRINT_L2("on_transfer_split called create_transactions_2");
1036
1037 return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
1038 res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
1039 }
1040 catch (const std::exception& e)
1041 {
1042 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR);
1043 return false;
1044 }
1045 return true;
1046 }
1047 //------------------------------------------------------------------------------------------------------------------------------
1048 bool wallet_rpc_server::on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1049 {
1050 if (!m_wallet) return not_open(er);
1051 if (m_restricted)
1052 {
1054 er.message = "Command unavailable in restricted mode.";
1055 return false;
1056 }
1057 if (m_wallet->key_on_device())
1058 {
1060 er.message = "command not supported by HW wallet";
1061 return false;
1062 }
1063 if(m_wallet->watch_only())
1064 {
1066 er.message = "command not supported by watch-only wallet";
1067 return false;
1068 }
1069
1071 if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob))
1072 {
1074 er.message = "Failed to parse hex.";
1075 return false;
1076 }
1077
1079 if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs))
1080 {
1082 er.message = "cannot load unsigned_txset";
1083 return false;
1084 }
1085
1086 std::vector<tools::wallet2::pending_tx> ptxs;
1087 try
1088 {
1090 std::string ciphertext = m_wallet->sign_tx_dump_to_str(exported_txs, ptxs, signed_txs);
1091 if (ciphertext.empty())
1092 {
1094 er.message = "Failed to sign unsigned tx";
1095 return false;
1096 }
1097
1098 res.signed_txset = epee::string_tools::buff_to_hex_nodelimer(ciphertext);
1099 }
1100 catch (const std::exception &e)
1101 {
1103 er.message = std::string("Failed to sign unsigned tx: ") + e.what();
1104 return false;
1105 }
1106
1107 for (auto &ptx: ptxs)
1108 {
1110 if (req.get_tx_keys)
1111 {
1112 res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
1113 for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
1114 res.tx_key_list.back() += epee::string_tools::pod_to_hex(additional_tx_key);
1115 }
1116 }
1117
1118 if (req.export_raw)
1119 {
1120 for (auto &ptx: ptxs)
1121 {
1123 }
1124 }
1125
1126 return true;
1127 }
1128 //------------------------------------------------------------------------------------------------------------------------------
1129 bool wallet_rpc_server::on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1130 {
1131 if (!m_wallet) return not_open(er);
1132 if (m_restricted)
1133 {
1135 er.message = "Command unavailable in restricted mode.";
1136 return false;
1137 }
1138 if (m_wallet->key_on_device())
1139 {
1141 er.message = "command not supported by HW wallet";
1142 return false;
1143 }
1144 if(m_wallet->watch_only())
1145 {
1147 er.message = "command not supported by watch-only wallet";
1148 return false;
1149 }
1150 if(req.unsigned_txset.empty() && req.multisig_txset.empty())
1151 {
1153 er.message = "no txset provided";
1154 return false;
1155 }
1156
1157 std::vector <wallet2::tx_construction_data> tx_constructions;
1158 if (!req.unsigned_txset.empty()) {
1159 try {
1162 if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) {
1164 er.message = "Failed to parse hex.";
1165 return false;
1166 }
1167 if (!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs)) {
1169 er.message = "cannot load unsigned_txset";
1170 return false;
1171 }
1172 tx_constructions = exported_txs.txes;
1173 }
1174 catch (const std::exception &e) {
1176 er.message = "failed to parse unsigned transfers: " + std::string(e.what());
1177 return false;
1178 }
1179 } else if (!req.multisig_txset.empty()) {
1180 try {
1183 if (!epee::string_tools::parse_hexstr_to_binbuff(req.multisig_txset, blob)) {
1185 er.message = "Failed to parse hex.";
1186 return false;
1187 }
1188 if (!m_wallet->parse_multisig_tx_from_str(blob, exported_txs)) {
1190 er.message = "cannot load multisig_txset";
1191 return false;
1192 }
1193
1194 for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n) {
1195 tx_constructions.push_back(exported_txs.m_ptx[n].construction_data);
1196 }
1197 }
1198 catch (const std::exception &e) {
1200 er.message = "failed to parse multisig transfers: " + std::string(e.what());
1201 return false;
1202 }
1203 }
1204
1205 std::vector<tools::wallet2::pending_tx> ptx;
1206 try
1207 {
1208 // gather info to ask the user
1209 std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
1210 int first_known_non_zero_change_index = -1;
1211 for (size_t n = 0; n < tx_constructions.size(); ++n)
1212 {
1213 const tools::wallet2::tx_construction_data &cd = tx_constructions[n];
1214 res.desc.push_back({0, 0, std::numeric_limits<uint32_t>::max(), 0, {}, "", 0, "", 0, 0, ""});
1215 wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::transfer_description &desc = res.desc.back();
1216
1217 std::vector<cryptonote::tx_extra_field> tx_extra_fields;
1218 bool has_encrypted_payment_id = false;
1219 crypto::hash8 payment_id8 = crypto::null_hash8;
1220 if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
1221 {
1222 cryptonote::tx_extra_nonce extra_nonce;
1223 if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
1224 {
1225 crypto::hash payment_id;
1227 {
1228 crypto::hash payment_id = crypto::null_hash;
1229 memcpy(payment_id.data, payment_id8.data, 8); // convert short pid to regular
1230 memset(payment_id.data + 8, 0, 24); // merely a sanity check
1231 desc.payment_id = epee::string_tools::pod_to_hex(payment_id);
1232
1233 }
1234 else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
1235 {
1236 desc.payment_id = epee::string_tools::pod_to_hex(payment_id);
1237 }
1238 }
1239 }
1240
1241 for (size_t s = 0; s < cd.sources.size(); ++s)
1242 {
1243 desc.amount_in += cd.sources[s].amount;
1244 size_t ring_size = cd.sources[s].outputs.size();
1245 if (ring_size < desc.ring_size)
1246 desc.ring_size = ring_size;
1247 }
1248 for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
1249 {
1251 std::string address = cryptonote::get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr);
1252 if (has_encrypted_payment_id && !entry.is_subaddress && address != entry.original)
1253 address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8);
1254 auto i = dests.find(entry.addr);
1255 if (i == dests.end())
1256 dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
1257 else
1258 i->second.second += entry.amount;
1259 desc.amount_out += entry.amount;
1260 }
1261 if (cd.change_dts.amount > 0)
1262 {
1263 auto it = dests.find(cd.change_dts.addr);
1264 if (it == dests.end())
1265 {
1267 er.message = "Claimed change does not go to a paid address";
1268 return false;
1269 }
1270 if (it->second.second < cd.change_dts.amount)
1271 {
1273 er.message = "Claimed change is larger than payment to the change address";
1274 return false;
1275 }
1276 if (cd.change_dts.amount > 0)
1277 {
1278 if (first_known_non_zero_change_index == -1)
1279 first_known_non_zero_change_index = n;
1280 const tools::wallet2::tx_construction_data &cdn = tx_constructions[first_known_non_zero_change_index];
1281 if (memcmp(&cd.change_dts.addr, &cdn.change_dts.addr, sizeof(cd.change_dts.addr)))
1282 {
1284 er.message = "Change goes to more than one address";
1285 return false;
1286 }
1287 }
1288 desc.change_amount += cd.change_dts.amount;
1289 it->second.second -= cd.change_dts.amount;
1290 if (it->second.second == 0)
1291 dests.erase(cd.change_dts.addr);
1292 }
1293
1294 size_t n_dummy_outputs = 0;
1295 for (auto i = dests.begin(); i != dests.end(); )
1296 {
1297 if (i->second.second > 0)
1298 {
1299 desc.recipients.push_back({i->second.first, i->second.second});
1300 }
1301 else
1302 ++desc.dummy_outputs;
1303 ++i;
1304 }
1305
1306 if (desc.change_amount > 0)
1307 {
1308 const tools::wallet2::tx_construction_data &cd0 = tx_constructions[0];
1309 desc.change_address = get_account_address_as_str(m_wallet->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr);
1310 }
1311
1312 desc.fee = desc.amount_in - desc.amount_out;
1313 desc.unlock_time = cd.unlock_time;
1314 desc.extra = epee::to_hex::string({cd.extra.data(), cd.extra.size()});
1315 }
1316 }
1317 catch (const std::exception &e)
1318 {
1320 er.message = "failed to parse unsigned transfers";
1321 return false;
1322 }
1323
1324 return true;
1325 }
1326 //------------------------------------------------------------------------------------------------------------------------------
1327 bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1328 {
1329 if (!m_wallet) return not_open(er);
1330 if (m_restricted)
1331 {
1333 er.message = "Command unavailable in restricted mode.";
1334 return false;
1335 }
1336 if (m_wallet->key_on_device())
1337 {
1339 er.message = "command not supported by HW wallet";
1340 return false;
1341 }
1342
1344 if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob))
1345 {
1347 er.message = "Failed to parse hex.";
1348 return false;
1349 }
1350
1351 std::vector<tools::wallet2::pending_tx> ptx_vector;
1352 try
1353 {
1354 bool r = m_wallet->parse_tx_from_str(blob, ptx_vector, NULL);
1355 if (!r)
1356 {
1358 er.message = "Failed to parse signed tx data.";
1359 return false;
1360 }
1361 }
1362 catch (const std::exception &e)
1363 {
1365 er.message = std::string("Failed to parse signed tx: ") + e.what();
1366 return false;
1367 }
1368
1369 try
1370 {
1371 for (auto &ptx: ptx_vector)
1372 {
1373 m_wallet->commit_tx(ptx);
1375 }
1376 }
1377 catch (const std::exception &e)
1378 {
1380 er.message = std::string("Failed to submit signed tx: ") + e.what();
1381 return false;
1382 }
1383
1384 return true;
1385 }
1386 //------------------------------------------------------------------------------------------------------------------------------
1387 bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1388 {
1389 if (!m_wallet) return not_open(er);
1390 if (m_restricted)
1391 {
1393 er.message = "Command unavailable in restricted mode.";
1394 return false;
1395 }
1396
1397 try
1398 {
1399 std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions();
1400
1401 return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
1402 res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
1403 }
1404 catch (const std::exception& e)
1405 {
1406 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR);
1407 return false;
1408 }
1409 return true;
1410 }
1411 //------------------------------------------------------------------------------------------------------------------------------
1412 bool wallet_rpc_server::on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1413 {
1414 std::vector<cryptonote::tx_destination_entry> dsts;
1415 std::vector<uint8_t> extra;
1416
1417 if (!m_wallet) return not_open(er);
1418 if (m_restricted)
1419 {
1421 er.message = "Command unavailable in restricted mode.";
1422 return false;
1423 }
1424
1425 // validate the transfer requested and populate dsts & extra
1426 std::list<wallet_rpc::transfer_destination> destination;
1427 destination.push_back(wallet_rpc::transfer_destination());
1428 destination.back().amount = 0;
1429 destination.back().address = req.address;
1430 if (!validate_transfer(destination, req.payment_id, dsts, extra, true, er))
1431 {
1432 return false;
1433 }
1434
1435 if (req.outputs < 1)
1436 {
1438 er.message = "Amount of outputs should be greater than 0.";
1439 return false;
1440 }
1441
1442 std::set<uint32_t> subaddr_indices;
1443 if (req.subaddr_indices_all)
1444 {
1445 for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(req.account_index); ++i)
1446 subaddr_indices.insert(i);
1447 }
1448 else
1449 {
1450 subaddr_indices= req.subaddr_indices;
1451 }
1452
1453 try
1454 {
1455 uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
1456 uint32_t priority = m_wallet->adjust_priority(req.priority);
1457 std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, subaddr_indices);
1458
1459 return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
1460 res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
1461 }
1462 catch (const std::exception& e)
1463 {
1464 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR);
1465 return false;
1466 }
1467 return true;
1468 }
1469//------------------------------------------------------------------------------------------------------------------------------
1470 bool wallet_rpc_server::on_sweep_single(const wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::request& req, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1471 {
1472 std::vector<cryptonote::tx_destination_entry> dsts;
1473 std::vector<uint8_t> extra;
1474
1475 if (!m_wallet) return not_open(er);
1476 if (m_restricted)
1477 {
1479 er.message = "Command unavailable in restricted mode.";
1480 return false;
1481 }
1482
1483 if (req.outputs < 1)
1484 {
1486 er.message = "Amount of outputs should be greater than 0.";
1487 return false;
1488 }
1489
1490 // validate the transfer requested and populate dsts & extra
1491 std::list<wallet_rpc::transfer_destination> destination;
1492 destination.push_back(wallet_rpc::transfer_destination());
1493 destination.back().amount = 0;
1494 destination.back().address = req.address;
1495 if (!validate_transfer(destination, req.payment_id, dsts, extra, true, er))
1496 {
1497 return false;
1498 }
1499
1501 if (!epee::string_tools::hex_to_pod(req.key_image, ki))
1502 {
1504 er.message = "failed to parse key image";
1505 return false;
1506 }
1507
1508 try
1509 {
1510 uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
1511 uint32_t priority = m_wallet->adjust_priority(req.priority);
1512 std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra);
1513
1514 if (ptx_vector.empty())
1515 {
1517 er.message = "No outputs found";
1518 return false;
1519 }
1520 if (ptx_vector.size() > 1)
1521 {
1523 er.message = "Multiple transactions are created, which is not supposed to happen";
1524 return false;
1525 }
1526 const wallet2::pending_tx &ptx = ptx_vector[0];
1527 if (ptx.selected_transfers.size() > 1)
1528 {
1530 er.message = "The transaction uses multiple inputs, which is not supposed to happen";
1531 return false;
1532 }
1533
1534 return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
1535 res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
1536 }
1537 catch (const std::exception& e)
1538 {
1539 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR);
1540 return false;
1541 }
1542 catch (...)
1543 {
1545 er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
1546 return false;
1547 }
1548 return true;
1549 }
1550 //------------------------------------------------------------------------------------------------------------------------------
1551 bool wallet_rpc_server::on_relay_tx(const wallet_rpc::COMMAND_RPC_RELAY_TX::request& req, wallet_rpc::COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1552 {
1553 if (!m_wallet) return not_open(er);
1554
1557 {
1559 er.message = "Failed to parse hex.";
1560 return false;
1561 }
1562
1564 try
1565 {
1566 std::istringstream iss(blob);
1568 ar >> ptx;
1569 }
1570 catch (...)
1571 {
1573 er.message = "Failed to parse tx metadata.";
1574 return false;
1575 }
1576
1577 try
1578 {
1579 m_wallet->commit_tx(ptx);
1580 }
1581 catch(const std::exception &e)
1582 {
1584 er.message = "Failed to commit tx.";
1585 return false;
1586 }
1587
1589
1590 return true;
1591 }
1592 //------------------------------------------------------------------------------------------------------------------------------
1593 bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1594 {
1595 if (!m_wallet) return not_open(er);
1596 try
1597 {
1598 crypto::hash8 payment_id;
1599 if (req.payment_id.empty())
1600 {
1601 payment_id = crypto::rand<crypto::hash8>();
1602 }
1603 else
1604 {
1605 if (!tools::wallet2::parse_short_payment_id(req.payment_id,payment_id))
1606 {
1608 er.message = "Invalid payment ID";
1609 return false;
1610 }
1611 }
1612
1613 if (req.standard_address.empty())
1614 {
1615 res.integrated_address = m_wallet->get_integrated_address_as_str(payment_id);
1616 }
1617 else
1618 {
1620 if(!get_account_address_from_str(info, m_wallet->nettype(), req.standard_address))
1621 {
1623 er.message = "Invalid address";
1624 return false;
1625 }
1626 if (info.is_subaddress)
1627 {
1629 er.message = "Subaddress shouldn't be used";
1630 return false;
1631 }
1632 if (info.has_payment_id)
1633 {
1635 er.message = "Already integrated address";
1636 return false;
1637 }
1638 if (req.payment_id.empty())
1639 {
1641 er.message = "Payment ID shouldn't be left unspecified";
1642 return false;
1643 }
1644 res.integrated_address = get_account_integrated_address_as_str(m_wallet->nettype(), info.address, payment_id);
1645 }
1646 res.payment_id = epee::string_tools::pod_to_hex(payment_id);
1647 return true;
1648 }
1649 catch (const std::exception& e)
1650 {
1651 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
1652 return false;
1653 }
1654 return true;
1655 }
1656 //------------------------------------------------------------------------------------------------------------------------------
1657 bool wallet_rpc_server::on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1658 {
1659 if (!m_wallet) return not_open(er);
1660 try
1661 {
1663
1664 if(!get_account_address_from_str(info, m_wallet->nettype(), req.integrated_address))
1665 {
1667 er.message = "Invalid address";
1668 return false;
1669 }
1670 if(!info.has_payment_id)
1671 {
1673 er.message = "Address is not an integrated address";
1674 return false;
1675 }
1676 res.standard_address = get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address);
1677 res.payment_id = epee::string_tools::pod_to_hex(info.payment_id);
1678 return true;
1679 }
1680 catch (const std::exception& e)
1681 {
1682 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
1683 return false;
1684 }
1685 return true;
1686 }
1687 //------------------------------------------------------------------------------------------------------------------------------
1688 bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1689 {
1690 if (!m_wallet) return not_open(er);
1691 if (m_restricted)
1692 {
1694 er.message = "Command unavailable in restricted mode.";
1695 return false;
1696 }
1697
1698 try
1699 {
1700 m_wallet->store();
1701 }
1702 catch (const std::exception& e)
1703 {
1704 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
1705 return false;
1706 }
1707 return true;
1708 }
1709 //------------------------------------------------------------------------------------------------------------------------------
1710 bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1711 {
1712 if (!m_wallet) return not_open(er);
1713 crypto::hash payment_id;
1714 crypto::hash8 payment_id8;
1715 cryptonote::blobdata payment_id_blob;
1716 if(!epee::string_tools::parse_hexstr_to_binbuff(req.payment_id, payment_id_blob))
1717 {
1719 er.message = "Payment ID has invalid format";
1720 return false;
1721 }
1722 // we always convert short IDs to long ones for the purposes of searching for payments
1723 if(sizeof(payment_id) == payment_id_blob.size())
1724 {
1725 payment_id = *reinterpret_cast<const crypto::hash*>(payment_id_blob.data());
1726 }
1727 else if(sizeof(payment_id8) == payment_id_blob.size())
1728 {
1729 payment_id8 = *reinterpret_cast<const crypto::hash8*>(payment_id_blob.data());
1730 memcpy(payment_id.data, payment_id8.data, 8);
1731 memset(payment_id.data + 8, 0, 24);
1732 }
1733 else
1734 {
1736 er.message = "Payment ID has invalid size: " + req.payment_id;
1737 return false;
1738 }
1739
1740 res.payments.clear();
1741 std::list<wallet2::payment_details> payment_list;
1742 m_wallet->get_payments(payment_id, payment_list);
1743 for (auto & payment : payment_list)
1744 {
1745 wallet_rpc::payment_details rpc_payment;
1746 rpc_payment.payment_id = req.payment_id;
1747 rpc_payment.tx_hash = epee::string_tools::pod_to_hex(payment.m_tx_hash);
1748 rpc_payment.amount = payment.m_amount;
1749 rpc_payment.block_height = payment.m_block_height;
1750 rpc_payment.unlock_time = payment.m_unlock_time;
1751 rpc_payment.subaddr_index = payment.m_subaddr_index;
1752 rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
1753 res.payments.push_back(rpc_payment);
1754 }
1755
1756 return true;
1757 }
1758 //------------------------------------------------------------------------------------------------------------------------------
1759 bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1760 {
1761 res.payments.clear();
1762 if (!m_wallet) return not_open(er);
1763
1764 /* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */
1765 if (req.payment_ids.empty())
1766 {
1767 std::list<std::pair<crypto::hash,wallet2::payment_details>> payment_list;
1768 m_wallet->get_payments(payment_list, req.min_block_height);
1769
1770 for (auto & payment : payment_list)
1771 {
1772 wallet_rpc::payment_details rpc_payment;
1773 rpc_payment.payment_id = epee::string_tools::pod_to_hex(payment.first);
1774 rpc_payment.tx_hash = epee::string_tools::pod_to_hex(payment.second.m_tx_hash);
1775 rpc_payment.amount = payment.second.m_amount;
1776 rpc_payment.block_height = payment.second.m_block_height;
1777 rpc_payment.unlock_time = payment.second.m_unlock_time;
1778 rpc_payment.subaddr_index = payment.second.m_subaddr_index;
1779 rpc_payment.address = m_wallet->get_subaddress_as_str(payment.second.m_subaddr_index);
1780 res.payments.push_back(std::move(rpc_payment));
1781 }
1782
1783 return true;
1784 }
1785
1786 for (auto & payment_id_str : req.payment_ids)
1787 {
1788 crypto::hash payment_id;
1789 crypto::hash8 payment_id8;
1790 cryptonote::blobdata payment_id_blob;
1791
1792 // TODO - should the whole thing fail because of one bad id?
1793 bool r;
1794 if (payment_id_str.size() == 2 * sizeof(payment_id))
1795 {
1796 r = epee::string_tools::hex_to_pod(payment_id_str, payment_id);
1797 }
1798 else if (payment_id_str.size() == 2 * sizeof(payment_id8))
1799 {
1800 r = epee::string_tools::hex_to_pod(payment_id_str, payment_id8);
1801 if (r)
1802 {
1803 memcpy(payment_id.data, payment_id8.data, 8);
1804 memset(payment_id.data + 8, 0, 24);
1805 }
1806 }
1807 else
1808 {
1810 er.message = "Payment ID has invalid size: " + payment_id_str;
1811 return false;
1812 }
1813
1814 if(!r)
1815 {
1817 er.message = "Payment ID has invalid format: " + payment_id_str;
1818 return false;
1819 }
1820
1821 std::list<wallet2::payment_details> payment_list;
1822 m_wallet->get_payments(payment_id, payment_list, req.min_block_height);
1823
1824 for (auto & payment : payment_list)
1825 {
1826 wallet_rpc::payment_details rpc_payment;
1827 rpc_payment.payment_id = payment_id_str;
1828 rpc_payment.tx_hash = epee::string_tools::pod_to_hex(payment.m_tx_hash);
1829 rpc_payment.amount = payment.m_amount;
1830 rpc_payment.block_height = payment.m_block_height;
1831 rpc_payment.unlock_time = payment.m_unlock_time;
1832 rpc_payment.subaddr_index = payment.m_subaddr_index;
1833 rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
1834 res.payments.push_back(std::move(rpc_payment));
1835 }
1836 }
1837
1838 return true;
1839 }
1840 //------------------------------------------------------------------------------------------------------------------------------
1841 bool wallet_rpc_server::on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1842 {
1843 if (!m_wallet) return not_open(er);
1844 if(req.transfer_type.compare("all") != 0 && req.transfer_type.compare("available") != 0 && req.transfer_type.compare("unavailable") != 0)
1845 {
1847 er.message = "Transfer type must be one of: all, available, or unavailable";
1848 return false;
1849 }
1850
1851 bool filter = false;
1852 bool available = false;
1853 if (req.transfer_type.compare("available") == 0)
1854 {
1855 filter = true;
1856 available = true;
1857 }
1858 else if (req.transfer_type.compare("unavailable") == 0)
1859 {
1860 filter = true;
1861 available = false;
1862 }
1863
1864 wallet2::transfer_container transfers;
1865 m_wallet->get_transfers(transfers);
1866
1867 bool transfers_found = false;
1868 for (const auto& td : transfers)
1869 {
1870 if (!filter || available != td.m_spent)
1871 {
1872 if (req.account_index != td.m_subaddr_index.major || (!req.subaddr_indices.empty() && req.subaddr_indices.count(td.m_subaddr_index.minor) == 0))
1873 continue;
1874 transfers_found = true;
1875 wallet_rpc::transfer_details rpc_transfers;
1876 rpc_transfers.amount = td.amount();
1877 rpc_transfers.spent = td.m_spent;
1878 rpc_transfers.global_index = td.m_global_output_index;
1879 rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid);
1880 rpc_transfers.subaddr_index = {td.m_subaddr_index.major, td.m_subaddr_index.minor};
1881 rpc_transfers.key_image = td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : "";
1882 rpc_transfers.block_height = td.m_block_height;
1883 rpc_transfers.frozen = td.m_frozen;
1884 rpc_transfers.unlocked = m_wallet->is_transfer_unlocked(td);
1885 res.transfers.push_back(rpc_transfers);
1886 }
1887 }
1888
1889 return true;
1890 }
1891 //------------------------------------------------------------------------------------------------------------------------------
1892 bool wallet_rpc_server::on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1893 {
1894 if (!m_wallet) return not_open(er);
1895 if (m_restricted)
1896 {
1898 er.message = "Command unavailable in restricted mode.";
1899 return false;
1900 }
1901
1902 if (req.key_type.compare("mnemonic") == 0)
1903 {
1905 bool ready;
1906 if (m_wallet->multisig(&ready))
1907 {
1908 if (!ready)
1909 {
1911 er.message = "This wallet is multisig, but not yet finalized";
1912 return false;
1913 }
1914 if (!m_wallet->get_multisig_seed(seed))
1915 {
1917 er.message = "Failed to get multisig seed.";
1918 return false;
1919 }
1920 }
1921 else
1922 {
1923 if (m_wallet->watch_only())
1924 {
1926 er.message = "The wallet is watch-only. Cannot retrieve seed.";
1927 return false;
1928 }
1929 if (!m_wallet->is_deterministic())
1930 {
1932 er.message = "The wallet is non-deterministic. Cannot display seed.";
1933 return false;
1934 }
1935 if (!m_wallet->get_seed(seed))
1936 {
1938 er.message = "Failed to get seed.";
1939 return false;
1940 }
1941 }
1942 res.key = std::string(seed.data(), seed.size()); // send to the network, then wipe RAM :D
1943 }
1944 else if(req.key_type.compare("view_key") == 0)
1945 {
1946 epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_view_secret_key);
1947 res.key = std::string(key.data(), key.size());
1948 }
1949 else if(req.key_type.compare("spend_key") == 0)
1950 {
1951 if (m_wallet->watch_only())
1952 {
1954 er.message = "The wallet is watch-only. Cannot retrieve spend key.";
1955 return false;
1956 }
1957 epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
1958 res.key = std::string(key.data(), key.size());
1959 }
1960 else
1961 {
1962 er.message = "key_type " + req.key_type + " not found";
1963 return false;
1964 }
1965
1966 return true;
1967 }
1968 //------------------------------------------------------------------------------------------------------------------------------
1969 bool wallet_rpc_server::on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1970 {
1971 if (!m_wallet) return not_open(er);
1972 if (m_restricted)
1973 {
1975 er.message = "Command unavailable in restricted mode.";
1976 return false;
1977 }
1978
1979 try
1980 {
1981 m_wallet->rescan_blockchain(req.hard);
1982 }
1983 catch (const std::exception& e)
1984 {
1985 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
1986 return false;
1987 }
1988 return true;
1989 }
1990 //------------------------------------------------------------------------------------------------------------------------------
1991 bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1992 {
1993 if (!m_wallet) return not_open(er);
1994 if (m_restricted)
1995 {
1997 er.message = "Command unavailable in restricted mode.";
1998 return false;
1999 }
2000
2001 res.signature = m_wallet->sign(req.data);
2002 return true;
2003 }
2004 //------------------------------------------------------------------------------------------------------------------------------
2005 bool wallet_rpc_server::on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2006 {
2007 if (!m_wallet) return not_open(er);
2008 if (m_restricted)
2009 {
2011 er.message = "Command unavailable in restricted mode.";
2012 return false;
2013 }
2014
2016 er.message = "";
2017 if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), req.address,
2018 [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
2019 if (!dnssec_valid)
2020 {
2021 er.message = std::string("Invalid DNSSEC for ") + url;
2022 return {};
2023 }
2024 if (addresses.empty())
2025 {
2026 er.message = std::string("No ETN address found at ") + url;
2027 return {};
2028 }
2029 return addresses[0];
2030 }))
2031 {
2033 return false;
2034 }
2035
2036 res.good = m_wallet->verify(req.data, info.address, req.signature);
2037 return true;
2038 }
2039 //------------------------------------------------------------------------------------------------------------------------------
2040 bool wallet_rpc_server::on_stop_wallet(const wallet_rpc::COMMAND_RPC_STOP_WALLET::request& req, wallet_rpc::COMMAND_RPC_STOP_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2041 {
2042 if (!m_wallet) return not_open(er);
2043 if (m_restricted)
2044 {
2046 er.message = "Command unavailable in restricted mode.";
2047 return false;
2048 }
2049
2050 try
2051 {
2052 m_wallet->store();
2053 m_stop.store(true, std::memory_order_relaxed);
2054 }
2055 catch (const std::exception& e)
2056 {
2057 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2058 return false;
2059 }
2060 return true;
2061 }
2062 //------------------------------------------------------------------------------------------------------------------------------
2063 bool wallet_rpc_server::on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2064 {
2065 if (!m_wallet) return not_open(er);
2066 if (m_restricted)
2067 {
2069 er.message = "Command unavailable in restricted mode.";
2070 return false;
2071 }
2072
2073 if (req.txids.size() != req.notes.size())
2074 {
2076 er.message = "Different amount of txids and notes";
2077 return false;
2078 }
2079
2080 std::list<crypto::hash> txids;
2081 std::list<std::string>::const_iterator i = req.txids.begin();
2082 while (i != req.txids.end())
2083 {
2084 cryptonote::blobdata txid_blob;
2085 if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash))
2086 {
2088 er.message = "TX ID has invalid format";
2089 return false;
2090 }
2091
2092 crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
2093 txids.push_back(txid);
2094 }
2095
2096 std::list<crypto::hash>::const_iterator il = txids.begin();
2097 std::list<std::string>::const_iterator in = req.notes.begin();
2098 while (il != txids.end())
2099 {
2100 m_wallet->set_tx_note(*il++, *in++);
2101 }
2102
2103 return true;
2104 }
2105 //------------------------------------------------------------------------------------------------------------------------------
2106 bool wallet_rpc_server::on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2107 {
2108 res.notes.clear();
2109 if (!m_wallet) return not_open(er);
2110
2111 std::list<crypto::hash> txids;
2112 std::list<std::string>::const_iterator i = req.txids.begin();
2113 while (i != req.txids.end())
2114 {
2115 cryptonote::blobdata txid_blob;
2116 if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash))
2117 {
2119 er.message = "TX ID has invalid format";
2120 return false;
2121 }
2122
2123 crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
2124 txids.push_back(txid);
2125 }
2126
2127 std::list<crypto::hash>::const_iterator il = txids.begin();
2128 while (il != txids.end())
2129 {
2130 res.notes.push_back(m_wallet->get_tx_note(*il++));
2131 }
2132 return true;
2133 }
2134 //------------------------------------------------------------------------------------------------------------------------------
2135 bool wallet_rpc_server::on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2136 {
2137 if (!m_wallet) return not_open(er);
2138 if (m_restricted)
2139 {
2141 er.message = "Command unavailable in restricted mode.";
2142 return false;
2143 }
2144
2145 m_wallet->set_attribute(req.key, req.value);
2146
2147 return true;
2148 }
2149 //------------------------------------------------------------------------------------------------------------------------------
2150 bool wallet_rpc_server::on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2151 {
2152 if (!m_wallet) return not_open(er);
2153 if (m_restricted)
2154 {
2156 er.message = "Command unavailable in restricted mode.";
2157 return false;
2158 }
2159
2160 res.value = m_wallet->get_attribute(req.key);
2161 return true;
2162 }
2163 bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2164 {
2165 if (!m_wallet) return not_open(er);
2166
2167 crypto::hash txid;
2168 if (!epee::string_tools::hex_to_pod(req.txid, txid))
2169 {
2171 er.message = "TX ID has invalid format";
2172 return false;
2173 }
2174
2175 crypto::secret_key tx_key;
2176 std::vector<crypto::secret_key> additional_tx_keys;
2177 if (!m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
2178 {
2180 er.message = "No tx secret key is stored for this tx";
2181 return false;
2182 }
2183
2185 s += epee::to_hex::wipeable_string(tx_key);
2186 for (size_t i = 0; i < additional_tx_keys.size(); ++i)
2187 s += epee::to_hex::wipeable_string(additional_tx_keys[i]);
2188 res.tx_key = std::string(s.data(), s.size());
2189 return true;
2190 }
2191 //------------------------------------------------------------------------------------------------------------------------------
2192 bool wallet_rpc_server::on_check_tx_key(const wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2193 {
2194 if (!m_wallet) return not_open(er);
2195
2196 crypto::hash txid;
2197 if (!epee::string_tools::hex_to_pod(req.txid, txid))
2198 {
2200 er.message = "TX ID has invalid format";
2201 return false;
2202 }
2203
2204 epee::wipeable_string tx_key_str = req.tx_key;
2205 if (tx_key_str.size() < 64 || tx_key_str.size() % 64)
2206 {
2208 er.message = "Tx key has invalid format";
2209 return false;
2210 }
2211 const char *data = tx_key_str.data();
2212 crypto::secret_key tx_key;
2213 if (!epee::wipeable_string(data, 64).hex_to_pod(unwrap(unwrap(tx_key))))
2214 {
2216 er.message = "Tx key has invalid format";
2217 return false;
2218 }
2219 size_t offset = 64;
2220 std::vector<crypto::secret_key> additional_tx_keys;
2221 while (offset < tx_key_str.size())
2222 {
2223 additional_tx_keys.resize(additional_tx_keys.size() + 1);
2224 if (!epee::wipeable_string(data + offset, 64).hex_to_pod(unwrap(unwrap(additional_tx_keys.back()))))
2225 {
2227 er.message = "Tx key has invalid format";
2228 return false;
2229 }
2230 offset += 64;
2231 }
2232
2234 if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
2235 {
2237 er.message = "Invalid address";
2238 return false;
2239 }
2240
2241 try
2242 {
2243 m_wallet->check_tx_key(txid, tx_key, additional_tx_keys, info.address, res.received, res.in_pool, res.confirmations);
2244 }
2245 catch (const std::exception &e)
2246 {
2248 er.message = e.what();
2249 return false;
2250 }
2251 return true;
2252 }
2253 //------------------------------------------------------------------------------------------------------------------------------
2254 bool wallet_rpc_server::on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2255 {
2256 if (!m_wallet) return not_open(er);
2257
2258 crypto::hash txid;
2259 if (!epee::string_tools::hex_to_pod(req.txid, txid))
2260 {
2262 er.message = "TX ID has invalid format";
2263 return false;
2264 }
2265
2267 if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
2268 {
2270 er.message = "Invalid address";
2271 return false;
2272 }
2273
2274 try
2275 {
2276 res.signature = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, req.message);
2277 }
2278 catch (const std::exception &e)
2279 {
2281 er.message = e.what();
2282 return false;
2283 }
2284 return true;
2285 }
2286 //------------------------------------------------------------------------------------------------------------------------------
2287 bool wallet_rpc_server::on_check_tx_proof(const wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2288 {
2289 if (!m_wallet) return not_open(er);
2290
2291 crypto::hash txid;
2292 if (!epee::string_tools::hex_to_pod(req.txid, txid))
2293 {
2295 er.message = "TX ID has invalid format";
2296 return false;
2297 }
2298
2300 if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
2301 {
2303 er.message = "Invalid address";
2304 return false;
2305 }
2306
2307 try
2308 {
2309 res.good = m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, req.message, req.signature, res.received, res.in_pool, res.confirmations);
2310 }
2311 catch (const std::exception &e)
2312 {
2314 er.message = e.what();
2315 return false;
2316 }
2317 return true;
2318 }
2319 //------------------------------------------------------------------------------------------------------------------------------
2320 bool wallet_rpc_server::on_get_spend_proof(const wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2321 {
2322 if (!m_wallet) return not_open(er);
2323
2324 crypto::hash txid;
2325 if (!epee::string_tools::hex_to_pod(req.txid, txid))
2326 {
2328 er.message = "TX ID has invalid format";
2329 return false;
2330 }
2331
2332 try
2333 {
2334 res.signature = m_wallet->get_spend_proof(txid, req.message);
2335 }
2336 catch (const std::exception &e)
2337 {
2339 er.message = e.what();
2340 return false;
2341 }
2342 return true;
2343 }
2344 //------------------------------------------------------------------------------------------------------------------------------
2345 bool wallet_rpc_server::on_check_spend_proof(const wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2346 {
2347 if (!m_wallet) return not_open(er);
2348
2349 crypto::hash txid;
2350 if (!epee::string_tools::hex_to_pod(req.txid, txid))
2351 {
2353 er.message = "TX ID has invalid format";
2354 return false;
2355 }
2356
2357 try
2358 {
2359 res.good = m_wallet->check_spend_proof(txid, req.message, req.signature);
2360 }
2361 catch (const std::exception &e)
2362 {
2364 er.message = e.what();
2365 return false;
2366 }
2367 return true;
2368 }
2369 //------------------------------------------------------------------------------------------------------------------------------
2370 bool wallet_rpc_server::on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2371 {
2372 if (!m_wallet) return not_open(er);
2373
2374 boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
2375 if (!req.all)
2376 {
2377 if (req.account_index >= m_wallet->get_num_subaddress_accounts())
2378 {
2380 er.message = "Account index is out of bound";
2381 return false;
2382 }
2383 account_minreserve = std::make_pair(req.account_index, req.amount);
2384 }
2385
2386 try
2387 {
2388 res.signature = m_wallet->get_reserve_proof(account_minreserve, req.message);
2389 }
2390 catch (const std::exception &e)
2391 {
2393 er.message = e.what();
2394 return false;
2395 }
2396 return true;
2397 }
2398 //------------------------------------------------------------------------------------------------------------------------------
2399 bool wallet_rpc_server::on_check_reserve_proof(const wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2400 {
2401 if (!m_wallet) return not_open(er);
2402
2404 if (!get_account_address_from_str(info, m_wallet->nettype(), req.address))
2405 {
2407 er.message = "Invalid address";
2408 return false;
2409 }
2410 if (info.is_subaddress)
2411 {
2413 er.message = "Address must not be a subaddress";
2414 return false;
2415 }
2416
2417 try
2418 {
2419 res.good = m_wallet->check_reserve_proof(info.address, req.message, req.signature, res.total, res.spent);
2420 }
2421 catch (const std::exception &e)
2422 {
2424 er.message = e.what();
2425 return false;
2426 }
2427 return true;
2428 }
2429 //------------------------------------------------------------------------------------------------------------------------------
2430 bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2431 {
2432 if (!m_wallet) return not_open(er);
2433 if (m_restricted)
2434 {
2436 er.message = "Command unavailable in restricted mode.";
2437 return false;
2438 }
2439
2440 uint64_t min_height = 0, max_height = CRYPTONOTE_MAX_BLOCK_NUMBER;
2441 if (req.filter_by_height)
2442 {
2443 min_height = req.min_height;
2444 max_height = req.max_height <= max_height ? req.max_height : max_height;
2445 }
2446
2447 boost::optional<uint32_t> account_index = req.account_index;
2448 std::set<uint32_t> subaddr_indices = req.subaddr_indices;
2449 if (req.all_accounts)
2450 {
2451 account_index = boost::none;
2452 subaddr_indices.clear();
2453 }
2454
2455 if (req.in)
2456 {
2457 std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
2458 m_wallet->get_payments(payments, min_height, max_height, account_index, subaddr_indices);
2459 for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2460 res.in.push_back(wallet_rpc::transfer_entry());
2461 fill_transfer_entry(res.in.back(), i->second.m_tx_hash, i->first, i->second);
2462 }
2463 }
2464
2465 if (req.out)
2466 {
2467 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
2468 m_wallet->get_payments_out(payments, min_height, max_height, account_index, subaddr_indices);
2469 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2470 res.out.push_back(wallet_rpc::transfer_entry());
2471 fill_transfer_entry(res.out.back(), i->first, i->second);
2472 }
2473 }
2474
2475 if (req.migration)
2476 {
2477 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
2478 m_wallet->get_payments_out_migration(payments, min_height, max_height, account_index, subaddr_indices);
2479 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2480 res.migration.push_back(wallet_rpc::transfer_entry());
2481 fill_transfer_entry(res.migration.back(), i->first, i->second);
2482 }
2483 }
2484
2485 if (req.sc_migration)
2486 {
2487 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
2488 m_wallet->get_payments_out_sc_migration(payments, min_height, max_height, account_index, subaddr_indices);
2489 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2490 res.sc_migration.push_back(wallet_rpc::transfer_entry());
2491 fill_transfer_entry(res.sc_migration.back(), i->first, i->second);
2492 }
2493 }
2494
2495 if (req.pending || req.failed) {
2496 std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
2497 m_wallet->get_unconfirmed_payments_out(upayments, account_index, subaddr_indices);
2498 for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
2501 if (!((req.failed && is_failed) || (!is_failed && req.pending)))
2502 continue;
2503 std::list<wallet_rpc::transfer_entry> &entries = is_failed ? res.failed : res.pending;
2504 entries.push_back(wallet_rpc::transfer_entry());
2505 fill_transfer_entry(entries.back(), i->first, i->second);
2506 }
2507 }
2508
2509 if (req.pool)
2510 {
2511 m_wallet->update_pool_state();
2512
2513 std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
2514 m_wallet->get_unconfirmed_payments(payments, account_index, subaddr_indices);
2515 for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2516 res.pool.push_back(wallet_rpc::transfer_entry());
2517 fill_transfer_entry(res.pool.back(), i->first, i->second);
2518 }
2519 }
2520
2521 return true;
2522 }
2523 //------------------------------------------------------------------------------------------------------------------------------
2524 bool wallet_rpc_server::on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2525 {
2526 if (!m_wallet) return not_open(er);
2527 if (m_restricted)
2528 {
2530 er.message = "Command unavailable in restricted mode.";
2531 return false;
2532 }
2533
2534 crypto::hash txid;
2535 cryptonote::blobdata txid_blob;
2536 if(!epee::string_tools::parse_hexstr_to_binbuff(req.txid, txid_blob))
2537 {
2539 er.message = "Transaction ID has invalid format";
2540 return false;
2541 }
2542
2543 if(sizeof(txid) == txid_blob.size())
2544 {
2545 txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
2546 }
2547 else
2548 {
2550 er.message = "Transaction ID has invalid size: " + req.txid;
2551 return false;
2552 }
2553
2554 if (req.account_index >= m_wallet->get_num_subaddress_accounts())
2555 {
2557 er.message = "Account index is out of bound";
2558 return false;
2559 }
2560
2561 std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
2562 m_wallet->get_payments(payments, 0, (uint64_t)-1, req.account_index);
2563 for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2564 if (i->second.m_tx_hash == txid)
2565 {
2566 res.transfers.resize(res.transfers.size() + 1);
2567 fill_transfer_entry(res.transfers.back(), i->second.m_tx_hash, i->first, i->second);
2568 }
2569 }
2570
2571 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out;
2572 m_wallet->get_payments_out(payments_out, 0, (uint64_t)-1, req.account_index);
2573 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
2574 if (i->first == txid)
2575 {
2576 res.transfers.resize(res.transfers.size() + 1);
2577 fill_transfer_entry(res.transfers.back(), i->first, i->second);
2578 }
2579 }
2580
2581 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> migrations;
2582 m_wallet->get_payments_out_migration(migrations, 0, (uint64_t)-1, req.account_index);
2583 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = migrations.begin(); i != migrations.end(); ++i) {
2584 if (i->first == txid)
2585 {
2586 res.transfers.resize(res.transfers.size() + 1);
2587 fill_transfer_entry(res.transfers.back(), i->first, i->second);
2588 }
2589 }
2590
2591 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> sc_migrations;
2592 m_wallet->get_payments_out_sc_migration(sc_migrations, 0, (uint64_t)-1, req.account_index);
2593 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = sc_migrations.begin(); i != sc_migrations.end(); ++i) {
2594 if (i->first == txid)
2595 {
2596 res.transfers.resize(res.transfers.size() + 1);
2597 fill_transfer_entry(res.transfers.back(), i->first, i->second);
2598 }
2599 }
2600
2601 std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
2602 m_wallet->get_unconfirmed_payments_out(upayments, req.account_index);
2603 for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
2604 if (i->first == txid)
2605 {
2606 res.transfers.resize(res.transfers.size() + 1);
2607 fill_transfer_entry(res.transfers.back(), i->first, i->second);
2608 }
2609 }
2610
2611 m_wallet->update_pool_state();
2612
2613 std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
2614 m_wallet->get_unconfirmed_payments(pool_payments, req.account_index);
2615 for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
2616 if (i->second.m_pd.m_tx_hash == txid)
2617 {
2618 res.transfers.resize(res.transfers.size() + 1);
2619 fill_transfer_entry(res.transfers.back(), i->first, i->second);
2620 }
2621 }
2622
2623 if (!res.transfers.empty())
2624 {
2625 res.transfer = res.transfers.front(); // backward compat
2626 return true;
2627 }
2628
2630 er.message = "Transaction not found.";
2631 return false;
2632 }
2633 //------------------------------------------------------------------------------------------------------------------------------
2634 bool wallet_rpc_server::on_export_outputs(const wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2635 {
2636 if (!m_wallet) return not_open(er);
2637 if (m_restricted)
2638 {
2640 er.message = "Command unavailable in restricted mode.";
2641 return false;
2642 }
2643 if (m_wallet->key_on_device())
2644 {
2646 er.message = "command not supported by HW wallet";
2647 return false;
2648 }
2649
2650 try
2651 {
2652 res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str(req.all));
2653 }
2654 catch (const std::exception &e)
2655 {
2656 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2657 return false;
2658 }
2659
2660 return true;
2661 }
2662 //------------------------------------------------------------------------------------------------------------------------------
2663 bool wallet_rpc_server::on_import_outputs(const wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2664 {
2665 if (!m_wallet) return not_open(er);
2666 if (m_restricted)
2667 {
2669 er.message = "Command unavailable in restricted mode.";
2670 return false;
2671 }
2672 if (m_wallet->key_on_device())
2673 {
2675 er.message = "command not supported by HW wallet";
2676 return false;
2677 }
2678
2680 if (!epee::string_tools::parse_hexstr_to_binbuff(req.outputs_data_hex, blob))
2681 {
2683 er.message = "Failed to parse hex.";
2684 return false;
2685 }
2686
2687 try
2688 {
2689 res.num_imported = m_wallet->import_outputs_from_str(blob);
2690 }
2691 catch (const std::exception &e)
2692 {
2693 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2694 return false;
2695 }
2696
2697 return true;
2698 }
2699 //------------------------------------------------------------------------------------------------------------------------------
2700 bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2701 {
2702 if (!m_wallet) return not_open(er);
2703 try
2704 {
2705 std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->export_key_images(req.all);
2706 res.offset = ski.first;
2707 res.signed_key_images.resize(ski.second.size());
2708 for (size_t n = 0; n < ski.second.size(); ++n)
2709 {
2710 res.signed_key_images[n].key_image = epee::string_tools::pod_to_hex(ski.second[n].first);
2711 res.signed_key_images[n].signature = epee::string_tools::pod_to_hex(ski.second[n].second);
2712 }
2713 }
2714
2715 catch (const std::exception& e)
2716 {
2717 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2718 return false;
2719 }
2720
2721 return true;
2722 }
2723 //------------------------------------------------------------------------------------------------------------------------------
2724 bool wallet_rpc_server::on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2725 {
2726 if (!m_wallet) return not_open(er);
2727 if (m_restricted)
2728 {
2730 er.message = "Command unavailable in restricted mode.";
2731 return false;
2732 }
2733 if (!m_wallet->is_trusted_daemon())
2734 {
2736 er.message = "This command requires a trusted daemon.";
2737 return false;
2738 }
2739 try
2740 {
2741 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
2742 ski.resize(req.signed_key_images.size());
2743 for (size_t n = 0; n < ski.size(); ++n)
2744 {
2745 if (!epee::string_tools::hex_to_pod(req.signed_key_images[n].key_image, ski[n].first))
2746 {
2748 er.message = "failed to parse key image";
2749 return false;
2750 }
2751
2752 if (!epee::string_tools::hex_to_pod(req.signed_key_images[n].signature, ski[n].second))
2753 {
2755 er.message = "failed to parse signature";
2756 return false;
2757 }
2758 }
2759 uint64_t spent = 0, unspent = 0;
2760 uint64_t height = m_wallet->import_key_images(ski, req.offset, spent, unspent);
2761 res.spent = spent;
2762 res.unspent = unspent;
2763 res.height = height;
2764 }
2765
2766 catch (const std::exception& e)
2767 {
2768 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2769 return false;
2770 }
2771
2772 return true;
2773 }
2774 //------------------------------------------------------------------------------------------------------------------------------
2775 bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2776 {
2777 if (!m_wallet) return not_open(er);
2778 std::string error;
2779 std::string uri = m_wallet->make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error);
2780 if (uri.empty())
2781 {
2783 er.message = std::string("Cannot make URI from supplied parameters: ") + error;
2784 return false;
2785 }
2786
2787 res.uri = uri;
2788 return true;
2789 }
2790 //------------------------------------------------------------------------------------------------------------------------------
2791 bool wallet_rpc_server::on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2792 {
2793 if (!m_wallet) return not_open(er);
2794 std::string error;
2795 if (!m_wallet->parse_uri(req.uri, res.uri.address, res.uri.payment_id, res.uri.amount, res.uri.tx_description, res.uri.recipient_name, res.unknown_parameters, error))
2796 {
2798 er.message = "Error parsing URI: " + error;
2799 return false;
2800 }
2801 return true;
2802 }
2803 //------------------------------------------------------------------------------------------------------------------------------
2804 bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2805 {
2806 if (!m_wallet) return not_open(er);
2807 const auto ab = m_wallet->get_address_book();
2808 if (req.entries.empty())
2809 {
2810 uint64_t idx = 0;
2811 for (const auto &entry: ab)
2812 res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
2813 }
2814 else
2815 {
2816 for (uint64_t idx: req.entries)
2817 {
2818 if (idx >= ab.size())
2819 {
2821 er.message = "Index out of range: " + std::to_string(idx);
2822 return false;
2823 }
2824 const auto &entry = ab[idx];
2825 res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
2826 }
2827 }
2828 return true;
2829 }
2830 //------------------------------------------------------------------------------------------------------------------------------
2831 bool wallet_rpc_server::on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2832 {
2833 if (!m_wallet) return not_open(er);
2834 if (m_restricted)
2835 {
2837 er.message = "Command unavailable in restricted mode.";
2838 return false;
2839 }
2840
2842 crypto::hash payment_id = crypto::null_hash;
2843 er.message = "";
2844 if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), req.address,
2845 [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
2846 if (!dnssec_valid)
2847 {
2848 er.message = std::string("Invalid DNSSEC for ") + url;
2849 return {};
2850 }
2851 if (addresses.empty())
2852 {
2853 er.message = std::string("No ETN address found at ") + url;
2854 return {};
2855 }
2856 return addresses[0];
2857 }))
2858 {
2860 if (er.message.empty())
2861 er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
2862 return false;
2863 }
2864 if (info.has_payment_id)
2865 {
2866 memcpy(payment_id.data, info.payment_id.data, 8);
2867 memset(payment_id.data + 8, 0, 24);
2868 }
2869 if (!req.payment_id.empty())
2870 {
2871 if (info.has_payment_id)
2872 {
2874 er.message = "Separate payment ID given with integrated address";
2875 return false;
2876 }
2877
2878 crypto::hash long_payment_id;
2879
2880 if (!wallet2::parse_long_payment_id(req.payment_id, payment_id))
2881 {
2882 if (!wallet2::parse_short_payment_id(req.payment_id, info.payment_id))
2883 {
2885 er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 64 character string";
2886 return false;
2887 }
2888 else
2889 {
2891 er.message = "Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address";
2892 return false;
2893 }
2894 }
2895 }
2896 if (!m_wallet->add_address_book_row(info.address, payment_id, req.description, info.is_subaddress))
2897 {
2899 er.message = "Failed to add address book entry";
2900 return false;
2901 }
2902 res.index = m_wallet->get_address_book().size() - 1;
2903 return true;
2904 }
2905 //------------------------------------------------------------------------------------------------------------------------------
2906 bool wallet_rpc_server::on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2907 {
2908 if (!m_wallet) return not_open(er);
2909 if (m_restricted)
2910 {
2912 er.message = "Command unavailable in restricted mode.";
2913 return false;
2914 }
2915
2916 const auto ab = m_wallet->get_address_book();
2917 if (req.index >= ab.size())
2918 {
2920 er.message = "Index out of range: " + std::to_string(req.index);
2921 return false;
2922 }
2923 if (!m_wallet->delete_address_book_row(req.index))
2924 {
2926 er.message = "Failed to delete address book entry";
2927 return false;
2928 }
2929 return true;
2930 }
2931 //------------------------------------------------------------------------------------------------------------------------------
2932 bool wallet_rpc_server::on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2933 {
2934 if (!m_wallet) return not_open(er);
2935 if (m_restricted)
2936 {
2938 er.message = "Command unavailable in restricted mode.";
2939 return false;
2940 }
2941 try
2942 {
2943 m_wallet->refresh(m_wallet->is_trusted_daemon(), req.start_height, res.blocks_fetched, res.received_etn);
2944 return true;
2945 }
2946 catch (const std::exception& e)
2947 {
2948 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2949 return false;
2950 }
2951 return true;
2952 }
2953 //------------------------------------------------------------------------------------------------------------------------------
2954 bool wallet_rpc_server::on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2955 {
2956 if (m_restricted)
2957 {
2959 er.message = "Command unavailable in restricted mode.";
2960 return false;
2961 }
2962 try
2963 {
2964 m_auto_refresh_period = req.enable ? req.period ? req.period : DEFAULT_AUTO_REFRESH_PERIOD : 0;
2965 MINFO("Auto refresh now " << (m_auto_refresh_period ? std::to_string(m_auto_refresh_period) + " seconds" : std::string("disabled")));
2966 return true;
2967 }
2968 catch (const std::exception& e)
2969 {
2970 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2971 return false;
2972 }
2973 return true;
2974 }
2975 //------------------------------------------------------------------------------------------------------------------------------
2976 bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2977 {
2978 if (!m_wallet) return not_open(er);
2979 if (m_restricted)
2980 {
2982 er.message = "Command unavailable in restricted mode.";
2983 return false;
2984 }
2985 try
2986 {
2987 m_wallet->rescan_spent();
2988 return true;
2989 }
2990 catch (const std::exception& e)
2991 {
2992 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2993 return false;
2994 }
2995 return true;
2996 }
2997 //------------------------------------------------------------------------------------------------------------------------------
2998 bool wallet_rpc_server::on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2999 {
3000 if (!m_wallet) return not_open(er);
3001 if (!m_wallet->is_trusted_daemon())
3002 {
3004 er.message = "This command requires a trusted daemon.";
3005 return false;
3006 }
3007
3008 size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2));
3009 if (req.threads_count < 1 || max_mining_threads_count < req.threads_count)
3010 {
3012 er.message = "The specified number of threads is inappropriate.";
3013 return false;
3014 }
3015
3017 daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3018 daemon_req.threads_count = req.threads_count;
3019 daemon_req.do_background_mining = req.do_background_mining;
3020 daemon_req.ignore_battery = req.ignore_battery;
3021
3023 bool r = m_wallet->invoke_http_json("/start_mining", daemon_req, daemon_res);
3024 if (!r || daemon_res.status != CORE_RPC_STATUS_OK)
3025 {
3027 er.message = "Couldn't start mining due to unknown error.";
3028 return false;
3029 }
3030 return true;
3031 }
3032 //------------------------------------------------------------------------------------------------------------------------------
3033 bool wallet_rpc_server::on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3034 {
3035 if (!m_wallet) return not_open(er);
3038 bool r = m_wallet->invoke_http_json("/stop_mining", daemon_req, daemon_res);
3039 if (!r || daemon_res.status != CORE_RPC_STATUS_OK)
3040 {
3042 er.message = "Couldn't stop mining due to unknown error.";
3043 return false;
3044 }
3045 return true;
3046 }
3047 //------------------------------------------------------------------------------------------------------------------------------
3048 bool wallet_rpc_server::on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3049 {
3051 crypto::ElectrumWords::get_language_list(res.languages_local, false);
3052 return true;
3053 }
3054 //------------------------------------------------------------------------------------------------------------------------------
3055 bool wallet_rpc_server::on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3056 {
3057 if (m_wallet_dir.empty())
3058 {
3060 er.message = "No wallet dir configured";
3061 return false;
3062 }
3063
3064 namespace po = boost::program_options;
3065 po::variables_map vm2;
3066 const char *ptr = strchr(req.filename.c_str(), '/');
3067#ifdef _WIN32
3068 if (!ptr)
3069 ptr = strchr(req.filename.c_str(), '\\');
3070 if (!ptr)
3071 ptr = strchr(req.filename.c_str(), ':');
3072#endif
3073 if (ptr)
3074 {
3076 er.message = "Invalid filename";
3077 return false;
3078 }
3079 std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
3080 {
3081 std::vector<std::string> languages;
3083 std::vector<std::string>::iterator it;
3084
3085 it = std::find(languages.begin(), languages.end(), req.language);
3086 if (it == languages.end())
3087 {
3089 it = std::find(languages.begin(), languages.end(), req.language);
3090 }
3091 if (it == languages.end())
3092 {
3094 er.message = "Unknown language: " + req.language;
3095 return false;
3096 }
3097 }
3098 {
3099 po::options_description desc("dummy");
3100 const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
3101 const char *argv[4];
3102 int argc = 3;
3103 argv[0] = "wallet-rpc";
3104 argv[1] = "--password";
3105 argv[2] = req.password.c_str();
3106 argv[3] = NULL;
3107 vm2 = *m_vm;
3108 command_line::add_arg(desc, arg_password);
3109 po::store(po::parse_command_line(argc, argv, desc), vm2);
3110 }
3111 std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, true, nullptr).first;
3112 if (!wal)
3113 {
3115 er.message = "Failed to create wallet";
3116 return false;
3117 }
3118 wal->set_seed_language(req.language);
3121 hres.height = 0;
3122 bool r = wal->invoke_http_json("/getheight", hreq, hres);
3123 if (r)
3124 wal->set_refresh_from_block_height(hres.height);
3125 crypto::secret_key dummy_key;
3126 try {
3127 wal->generate(wallet_file, req.password, dummy_key, false, false);
3128 }
3129 catch (const std::exception& e)
3130 {
3131 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3132 return false;
3133 }
3134 if (!wal)
3135 {
3137 er.message = "Failed to generate wallet";
3138 return false;
3139 }
3140
3141 if (m_wallet)
3142 {
3143 try
3144 {
3145 m_wallet->store();
3146 }
3147 catch (const std::exception& e)
3148 {
3149 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3150 return false;
3151 }
3152 delete m_wallet;
3153 }
3154 m_wallet = wal.release();
3155 return true;
3156 }
3157 //------------------------------------------------------------------------------------------------------------------------------
3158 bool wallet_rpc_server::on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3159 {
3160 if (m_wallet_dir.empty())
3161 {
3163 er.message = "No wallet dir configured";
3164 return false;
3165 }
3166
3167 namespace po = boost::program_options;
3168 po::variables_map vm2;
3169 const char *ptr = strchr(req.filename.c_str(), '/');
3170#ifdef _WIN32
3171 if (!ptr)
3172 ptr = strchr(req.filename.c_str(), '\\');
3173 if (!ptr)
3174 ptr = strchr(req.filename.c_str(), ':');
3175#endif
3176 if (ptr)
3177 {
3179 er.message = "Invalid filename";
3180 return false;
3181 }
3182 if (m_wallet && req.autosave_current)
3183 {
3184 try
3185 {
3186 m_wallet->store();
3187 }
3188 catch (const std::exception& e)
3189 {
3190 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3191 return false;
3192 }
3193 }
3194 std::string wallet_file = m_wallet_dir + "/" + req.filename;
3195 {
3196 po::options_description desc("dummy");
3197 const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
3198 const char *argv[4];
3199 int argc = 3;
3200 argv[0] = "wallet-rpc";
3201 argv[1] = "--password";
3202 argv[2] = req.password.c_str();
3203 argv[3] = NULL;
3204 vm2 = *m_vm;
3205 command_line::add_arg(desc, arg_password);
3206 po::store(po::parse_command_line(argc, argv, desc), vm2);
3207 }
3208 std::unique_ptr<tools::wallet2> wal = nullptr;
3209 try {
3210 wal = tools::wallet2::make_from_file(vm2, true, wallet_file, nullptr).first;
3211 }
3212 catch (const std::exception& e)
3213 {
3214 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3215 }
3216 if (!wal)
3217 {
3219 er.message = "Failed to open wallet";
3220 return false;
3221 }
3222
3223 if (m_wallet)
3224 delete m_wallet;
3225 m_wallet = wal.release();
3226 return true;
3227 }
3228 //------------------------------------------------------------------------------------------------------------------------------
3229 bool wallet_rpc_server::on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3230 {
3231 if (!m_wallet) return not_open(er);
3232
3233 if (req.autosave_current)
3234 {
3235 try
3236 {
3237 m_wallet->store();
3238 }
3239 catch (const std::exception& e)
3240 {
3241 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3242 return false;
3243 }
3244 }
3245 delete m_wallet;
3246 m_wallet = NULL;
3247 return true;
3248 }
3249 //------------------------------------------------------------------------------------------------------------------------------
3250 bool wallet_rpc_server::on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3251 {
3252 if (!m_wallet) return not_open(er);
3253 if (m_restricted)
3254 {
3256 er.message = "Command unavailable in restricted mode.";
3257 return false;
3258 }
3259 if (m_wallet->verify_password(req.old_password))
3260 {
3261 try
3262 {
3263 m_wallet->change_password(m_wallet->get_wallet_file(), req.old_password, req.new_password);
3264 LOG_PRINT_L0("Wallet password changed.");
3265 }
3266 catch (const std::exception& e)
3267 {
3268 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3269 return false;
3270 }
3271 }
3272 else
3273 {
3275 er.message = "Invalid original password.";
3276 return false;
3277 }
3278 return true;
3279 }
3280 //------------------------------------------------------------------------------------------------------------------------------
3281 void wallet_rpc_server::handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code) {
3282 try
3283 {
3284 std::rethrow_exception(e);
3285 }
3287 {
3289 er.message = e.what();
3290 }
3291 catch (const tools::error::daemon_busy& e)
3292 {
3294 er.message = e.what();
3295 }
3296 catch (const tools::error::zero_destination& e)
3297 {
3299 er.message = e.what();
3300 }
3301 catch (const tools::error::not_enough_etn& e)
3302 {
3304 er.message = e.what();
3305 }
3307 {
3309 er.message = e.what();
3310 }
3311 catch (const tools::error::tx_not_possible& e)
3312 {
3314 er.message = (boost::format(tr("Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee)")) %
3318 cryptonote::print_etn(e.fee())).str();
3319 er.message = e.what();
3320 }
3322 {
3324 er.message = e.what() + std::string(" Please use sweep_dust.");
3325 }
3326 catch (const error::file_exists& e)
3327 {
3329 er.message = "Cannot create wallet. Already exists.";
3330 }
3331 catch (const error::invalid_password& e)
3332 {
3334 er.message = "Invalid password.";
3335 }
3336 catch (const error::account_index_outofbound& e)
3337 {
3339 er.message = e.what();
3340 }
3341 catch (const error::address_index_outofbound& e)
3342 {
3344 er.message = e.what();
3345 }
3346 catch (const error::signature_check_failed& e)
3347 {
3349 er.message = e.what();
3350 }
3351 catch (const std::exception& e)
3352 {
3353 er.code = default_error_code;
3354 er.message = e.what();
3355 }
3356 catch (...)
3357 {
3359 er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
3360 }
3361 }
3362 //------------------------------------------------------------------------------------------------------------------------------
3363 bool wallet_rpc_server::on_generate_from_keys(const wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::request &req, wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::response &res, epee::json_rpc::error &er, const connection_context *ctx)
3364 {
3365 if (m_wallet_dir.empty())
3366 {
3368 er.message = "No wallet dir configured";
3369 return false;
3370 }
3371
3372 // early check for mandatory fields
3373 if (req.viewkey.empty())
3374 {
3376 er.message = "field 'viewkey' is mandatory. Please provide a view key you want to restore from.";
3377 return false;
3378 }
3379 if (req.address.empty())
3380 {
3382 er.message = "field 'address' is mandatory. Please provide a public address.";
3383 return false;
3384 }
3385
3386 namespace po = boost::program_options;
3387 po::variables_map vm2;
3388 const char *ptr = strchr(req.filename.c_str(), '/');
3389 #ifdef _WIN32
3390 if (!ptr)
3391 ptr = strchr(req.filename.c_str(), '\\');
3392 if (!ptr)
3393 ptr = strchr(req.filename.c_str(), ':');
3394 #endif
3395 if (ptr)
3396 {
3398 er.message = "Invalid filename";
3399 return false;
3400 }
3401 std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
3402 // check if wallet file already exists
3403 if (!wallet_file.empty())
3404 {
3405 try
3406 {
3407 boost::system::error_code ignored_ec;
3408 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(wallet_file, ignored_ec), error::file_exists, wallet_file);
3409 }
3410 catch (const std::exception &e)
3411 {
3413 er.message = "Wallet already exists.";
3414 return false;
3415 }
3416 }
3417
3418 {
3419 po::options_description desc("dummy");
3420 const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
3421 const char *argv[4];
3422 int argc = 3;
3423 argv[0] = "wallet-rpc";
3424 argv[1] = "--password";
3425 argv[2] = req.password.c_str();
3426 argv[3] = NULL;
3427 vm2 = *m_vm;
3428 command_line::add_arg(desc, arg_password);
3429 po::store(po::parse_command_line(argc, argv, desc), vm2);
3430 }
3431
3432 auto rc = tools::wallet2::make_new(vm2, true, nullptr);
3433 std::unique_ptr<wallet2> wal;
3434 wal = std::move(rc.first);
3435 if (!wal)
3436 {
3438 er.message = "Failed to create wallet";
3439 return false;
3440 }
3441
3443 if(!get_account_address_from_str(info, wal->nettype(), req.address))
3444 {
3446 er.message = "Failed to parse public address";
3447 return false;
3448 }
3449
3450 epee::wipeable_string password = rc.second.password();
3451 epee::wipeable_string viewkey_string = req.viewkey;
3452 crypto::secret_key viewkey;
3453 if (!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
3454 {
3456 er.message = "Failed to parse view key secret key";
3457 return false;
3458 }
3459
3460 if (m_wallet && req.autosave_current)
3461 {
3462 try
3463 {
3464 if (!wallet_file.empty())
3465 m_wallet->store();
3466 }
3467 catch (const std::exception &e)
3468 {
3469 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3470 return false;
3471 }
3472 }
3473
3474 try
3475 {
3476 if (!req.spendkey.empty())
3477 {
3478 epee::wipeable_string spendkey_string = req.spendkey;
3480 if (!spendkey_string.hex_to_pod(unwrap(unwrap(spendkey))))
3481 {
3483 er.message = "Failed to parse spend key secret key";
3484 return false;
3485 }
3486 wal->generate(wallet_file, std::move(rc.second).password(), info.address, spendkey, viewkey, false);
3487 res.info = "Wallet has been generated successfully.";
3488 }
3489 else
3490 {
3491 wal->generate(wallet_file, std::move(rc.second).password(), info.address, viewkey, false);
3492 res.info = "Watch-only wallet has been generated successfully.";
3493 }
3494 MINFO("Wallet has been generated.\n");
3495 }
3496 catch (const std::exception &e)
3497 {
3498 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3499 return false;
3500 }
3501
3502 if (!wal)
3503 {
3505 er.message = "Failed to generate wallet";
3506 return false;
3507 }
3508
3509 // set blockheight if given
3510 try
3511 {
3512 wal->set_refresh_from_block_height(req.restore_height);
3513 wal->rewrite(wallet_file, password);
3514 }
3515 catch (const std::exception &e)
3516 {
3517 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3518 return false;
3519 }
3520
3521 if (m_wallet)
3522 delete m_wallet;
3523 m_wallet = wal.release();
3524 res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3525 return true;
3526 }
3527 //------------------------------------------------------------------------------------------------------------------------------
3528 bool wallet_rpc_server::on_restore_deterministic_wallet(const wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::request &req, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::response &res, epee::json_rpc::error &er, const connection_context *ctx)
3529 {
3530 if (m_wallet_dir.empty())
3531 {
3533 er.message = "No wallet dir configured";
3534 return false;
3535 }
3536
3537 // early check for mandatory fields
3538 if (req.seed.empty())
3539 {
3541 er.message = "field 'seed' is mandatory. Please provide a seed you want to restore from.";
3542 return false;
3543 }
3544
3545 namespace po = boost::program_options;
3546 po::variables_map vm2;
3547 const char *ptr = strchr(req.filename.c_str(), '/');
3548 #ifdef _WIN32
3549 if (!ptr)
3550 ptr = strchr(req.filename.c_str(), '\\');
3551 if (!ptr)
3552 ptr = strchr(req.filename.c_str(), ':');
3553 #endif
3554 if (ptr)
3555 {
3557 er.message = "Invalid filename";
3558 return false;
3559 }
3560 std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
3561 // check if wallet file already exists
3562 if (!wallet_file.empty())
3563 {
3564 try
3565 {
3566 boost::system::error_code ignored_ec;
3567 THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(wallet_file, ignored_ec), error::file_exists, wallet_file);
3568 }
3569 catch (const std::exception &e)
3570 {
3572 er.message = "Wallet already exists.";
3573 return false;
3574 }
3575 }
3576 crypto::secret_key recovery_key;
3577 std::string old_language;
3578
3579 // check the given seed
3580 {
3581 if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, old_language))
3582 {
3584 er.message = "Electrum-style word list failed verification";
3585 return false;
3586 }
3587 }
3588 if (m_wallet && req.autosave_current)
3589 {
3590 try
3591 {
3592 m_wallet->store();
3593 }
3594 catch (const std::exception &e)
3595 {
3596 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3597 return false;
3598 }
3599 }
3600
3601 // process seed_offset if given
3602 {
3603 if (!req.seed_offset.empty())
3604 {
3605 recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset);
3606 }
3607 }
3608 {
3609 po::options_description desc("dummy");
3610 const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
3611 const char *argv[4];
3612 int argc = 3;
3613 argv[0] = "wallet-rpc";
3614 argv[1] = "--password";
3615 argv[2] = req.password.c_str();
3616 argv[3] = NULL;
3617 vm2 = *m_vm;
3618 command_line::add_arg(desc, arg_password);
3619 po::store(po::parse_command_line(argc, argv, desc), vm2);
3620 }
3621
3622 auto rc = tools::wallet2::make_new(vm2, true, nullptr);
3623 std::unique_ptr<wallet2> wal;
3624 wal = std::move(rc.first);
3625 if (!wal)
3626 {
3628 er.message = "Failed to create wallet";
3629 return false;
3630 }
3631
3632 epee::wipeable_string password = rc.second.password();
3633
3634 bool was_deprecated_wallet = ((old_language == crypto::ElectrumWords::old_language_name) ||
3636
3637 std::string mnemonic_language = old_language;
3638 if (was_deprecated_wallet)
3639 {
3640 // The user had used an older version of the wallet with old style mnemonics.
3641 res.was_deprecated = true;
3642 }
3643
3644 if (old_language == crypto::ElectrumWords::old_language_name)
3645 {
3646 if (req.language.empty())
3647 {
3649 er.message = "Wallet was using the old seed language. You need to specify a new seed language.";
3650 return false;
3651 }
3652 std::vector<std::string> language_list;
3653 std::vector<std::string> language_list_en;
3655 crypto::ElectrumWords::get_language_list(language_list_en, true);
3656 if (std::find(language_list.begin(), language_list.end(), req.language) == language_list.end() &&
3657 std::find(language_list_en.begin(), language_list_en.end(), req.language) == language_list_en.end())
3658 {
3660 er.message = "Wallet was using the old seed language, and the specified new seed language is invalid.";
3661 return false;
3662 }
3663 mnemonic_language = req.language;
3664 }
3665
3666 wal->set_seed_language(mnemonic_language);
3667
3668 crypto::secret_key recovery_val;
3669 try
3670 {
3671 recovery_val = wal->generate(wallet_file, std::move(rc.second).password(), recovery_key, true, false, false);
3672 MINFO("Wallet has been restored.\n");
3673 }
3674 catch (const std::exception &e)
3675 {
3676 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3677 return false;
3678 }
3679
3680 // // Convert the secret key back to seed
3681 epee::wipeable_string electrum_words;
3682 if (!crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language))
3683 {
3685 er.message = "Failed to encode seed";
3686 return false;
3687 }
3688 res.seed = std::string(electrum_words.data(), electrum_words.size());
3689
3690 if (!wal)
3691 {
3693 er.message = "Failed to generate wallet";
3694 return false;
3695 }
3696
3697 // set blockheight if given
3698 try
3699 {
3700 wal->set_refresh_from_block_height(req.restore_height);
3701 wal->rewrite(wallet_file, password);
3702 }
3703 catch (const std::exception &e)
3704 {
3705 handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3706 return false;
3707 }
3708
3709 if (m_wallet)
3710 delete m_wallet;
3711 m_wallet = wal.release();
3712 res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3713 res.info = "Wallet has been restored successfully.";
3714 return true;
3715 }
3716 //------------------------------------------------------------------------------------------------------------------------------
3717 bool wallet_rpc_server::on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3718 {
3719 if (!m_wallet) return not_open(er);
3720 res.multisig = m_wallet->multisig(&res.ready, &res.threshold, &res.total);
3721 return true;
3722 }
3723 //------------------------------------------------------------------------------------------------------------------------------
3724 bool wallet_rpc_server::on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3725 {
3726 if (!m_wallet) return not_open(er);
3727 if (m_restricted)
3728 {
3730 er.message = "Command unavailable in restricted mode.";
3731 return false;
3732 }
3733 if (m_wallet->multisig())
3734 {
3736 er.message = "This wallet is already multisig";
3737 return false;
3738 }
3739 if (m_wallet->watch_only())
3740 {
3742 er.message = "wallet is watch-only and cannot be made multisig";
3743 return false;
3744 }
3745
3746 res.multisig_info = m_wallet->get_multisig_info();
3747 return true;
3748 }
3749 //------------------------------------------------------------------------------------------------------------------------------
3750 bool wallet_rpc_server::on_make_multisig(const wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3751 {
3752 if (!m_wallet) return not_open(er);
3753 if (m_restricted)
3754 {
3756 er.message = "Command unavailable in restricted mode.";
3757 return false;
3758 }
3759 if (m_wallet->multisig())
3760 {
3762 er.message = "This wallet is already multisig";
3763 return false;
3764 }
3765 if (m_wallet->watch_only())
3766 {
3768 er.message = "wallet is watch-only and cannot be made multisig";
3769 return false;
3770 }
3771
3772 try
3773 {
3774 res.multisig_info = m_wallet->make_multisig(req.password, req.multisig_info, req.threshold);
3775 res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3776 }
3777 catch (const std::exception &e)
3778 {
3780 er.message = e.what();
3781 return false;
3782 }
3783
3784 return true;
3785 }
3786 //------------------------------------------------------------------------------------------------------------------------------
3787 bool wallet_rpc_server::on_export_multisig(const wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3788 {
3789 if (!m_wallet) return not_open(er);
3790 if (m_restricted)
3791 {
3793 er.message = "Command unavailable in restricted mode.";
3794 return false;
3795 }
3796 bool ready;
3797 if (!m_wallet->multisig(&ready))
3798 {
3800 er.message = "This wallet is not multisig";
3801 return false;
3802 }
3803 if (!ready)
3804 {
3806 er.message = "This wallet is multisig, but not yet finalized";
3807 return false;
3808 }
3809
3811 try
3812 {
3813 info = m_wallet->export_multisig();
3814 }
3815 catch (const std::exception &e)
3816 {
3818 er.message = e.what();
3819 return false;
3820 }
3821
3823
3824 return true;
3825 }
3826 //------------------------------------------------------------------------------------------------------------------------------
3827 bool wallet_rpc_server::on_import_multisig(const wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3828 {
3829 if (!m_wallet) return not_open(er);
3830 if (m_restricted)
3831 {
3833 er.message = "Command unavailable in restricted mode.";
3834 return false;
3835 }
3836 bool ready;
3837 uint32_t threshold, total;
3838 if (!m_wallet->multisig(&ready, &threshold, &total))
3839 {
3841 er.message = "This wallet is not multisig";
3842 return false;
3843 }
3844 if (!ready)
3845 {
3847 er.message = "This wallet is multisig, but not yet finalized";
3848 return false;
3849 }
3850
3851 if (req.info.size() < threshold - 1)
3852 {
3854 er.message = "Needs multisig export info from more participants";
3855 return false;
3856 }
3857
3858 std::vector<cryptonote::blobdata> info;
3859 info.resize(req.info.size());
3860 for (size_t n = 0; n < info.size(); ++n)
3861 {
3863 {
3865 er.message = "Failed to parse hex.";
3866 return false;
3867 }
3868 }
3869
3870 try
3871 {
3872 res.n_outputs = m_wallet->import_multisig(info);
3873 }
3874 catch (const std::exception &e)
3875 {
3877 er.message = "Error calling import_multisig";
3878 return false;
3879 }
3880
3881 if (m_wallet->is_trusted_daemon())
3882 {
3883 try
3884 {
3885 m_wallet->rescan_spent();
3886 }
3887 catch (const std::exception &e)
3888 {
3889 er.message = std::string("Success, but failed to update spent status after import multisig info: ") + e.what();
3890 }
3891 }
3892 else
3893 {
3894 er.message = "Success, but cannot update spent status after import multisig info as daemon is untrusted";
3895 }
3896
3897 return true;
3898 }
3899 //------------------------------------------------------------------------------------------------------------------------------
3900 bool wallet_rpc_server::on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3901 {
3902 if (!m_wallet) return not_open(er);
3903 if (m_restricted)
3904 {
3906 er.message = "Command unavailable in restricted mode.";
3907 return false;
3908 }
3909 bool ready;
3910 uint32_t threshold, total;
3911 if (!m_wallet->multisig(&ready, &threshold, &total))
3912 {
3914 er.message = "This wallet is not multisig";
3915 return false;
3916 }
3917 if (ready)
3918 {
3920 er.message = "This wallet is multisig, and already finalized";
3921 return false;
3922 }
3923
3924 if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
3925 {
3927 er.message = "Needs multisig info from more participants";
3928 return false;
3929 }
3930
3931 try
3932 {
3933 if (!m_wallet->finalize_multisig(req.password, req.multisig_info))
3934 {
3936 er.message = "Error calling finalize_multisig";
3937 return false;
3938 }
3939 }
3940 catch (const std::exception &e)
3941 {
3943 er.message = std::string("Error calling finalize_multisig: ") + e.what();
3944 return false;
3945 }
3946 res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3947
3948 return true;
3949 }
3950 //------------------------------------------------------------------------------------------------------------------------------
3951 bool wallet_rpc_server::on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
3952 {
3953 if (!m_wallet) return not_open(er);
3954 if (m_restricted)
3955 {
3957 er.message = "Command unavailable in restricted mode.";
3958 return false;
3959 }
3960 bool ready;
3961 uint32_t threshold, total;
3962 if (!m_wallet->multisig(&ready, &threshold, &total))
3963 {
3965 er.message = "This wallet is not multisig";
3966 return false;
3967 }
3968
3969 if (ready)
3970 {
3972 er.message = "This wallet is multisig, and already finalized";
3973 return false;
3974 }
3975
3976 if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
3977 {
3979 er.message = "Needs multisig info from more participants";
3980 return false;
3981 }
3982
3983 try
3984 {
3985 res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info);
3986 if (res.multisig_info.empty())
3987 {
3988 res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3989 }
3990 }
3991 catch (const std::exception &e)
3992 {
3994 er.message = std::string("Error calling exchange_multisig_info: ") + e.what();
3995 return false;
3996 }
3997 return true;
3998 }
3999 //------------------------------------------------------------------------------------------------------------------------------
4000 bool wallet_rpc_server::on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
4001 {
4002 if (!m_wallet) return not_open(er);
4003 if (m_restricted)
4004 {
4006 er.message = "Command unavailable in restricted mode.";
4007 return false;
4008 }
4009 bool ready;
4010 uint32_t threshold, total;
4011 if (!m_wallet->multisig(&ready, &threshold, &total))
4012 {
4014 er.message = "This wallet is not multisig";
4015 return false;
4016 }
4017 if (!ready)
4018 {
4020 er.message = "This wallet is multisig, but not yet finalized";
4021 return false;
4022 }
4023
4025 if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob))
4026 {
4028 er.message = "Failed to parse hex.";
4029 return false;
4030 }
4031
4033 bool r = m_wallet->load_multisig_tx(blob, txs, NULL);
4034 if (!r)
4035 {
4037 er.message = "Failed to parse multisig tx data.";
4038 return false;
4039 }
4040
4041 std::vector<crypto::hash> txids;
4042 try
4043 {
4044 bool r = m_wallet->sign_multisig_tx(txs, txids);
4045 if (!r)
4046 {
4048 er.message = "Failed to sign multisig tx";
4049 return false;
4050 }
4051 }
4052 catch (const std::exception &e)
4053 {
4055 er.message = std::string("Failed to sign multisig tx: ") + e.what();
4056 return false;
4057 }
4058
4059 res.tx_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(txs));
4060 if (!txids.empty())
4061 {
4062 for (const crypto::hash &txid: txids)
4063 res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(txid));
4064 }
4065
4066 return true;
4067 }
4068 //------------------------------------------------------------------------------------------------------------------------------
4069 bool wallet_rpc_server::on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
4070 {
4071 if (!m_wallet) return not_open(er);
4072 if (m_restricted)
4073 {
4075 er.message = "Command unavailable in restricted mode.";
4076 return false;
4077 }
4078 bool ready;
4079 uint32_t threshold, total;
4080 if (!m_wallet->multisig(&ready, &threshold, &total))
4081 {
4083 er.message = "This wallet is not multisig";
4084 return false;
4085 }
4086 if (!ready)
4087 {
4089 er.message = "This wallet is multisig, but not yet finalized";
4090 return false;
4091 }
4092
4094 if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob))
4095 {
4097 er.message = "Failed to parse hex.";
4098 return false;
4099 }
4100
4102 bool r = m_wallet->load_multisig_tx(blob, txs, NULL);
4103 if (!r)
4104 {
4106 er.message = "Failed to parse multisig tx data.";
4107 return false;
4108 }
4109
4110 if (txs.m_signers.size() < threshold)
4111 {
4113 er.message = "Not enough signers signed this transaction.";
4114 return false;
4115 }
4116
4117 try
4118 {
4119 for (auto &ptx: txs.m_ptx)
4120 {
4121 m_wallet->commit_tx(ptx);
4123 }
4124 }
4125 catch (const std::exception &e)
4126 {
4128 er.message = std::string("Failed to submit multisig tx: ") + e.what();
4129 return false;
4130 }
4131
4132 return true;
4133 }
4134 //------------------------------------------------------------------------------------------------------------------------------
4135 bool wallet_rpc_server::on_validate_address(const wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
4136 {
4138 static const struct { cryptonote::network_type type; const char *stype; } net_types[] = {
4139 { cryptonote::MAINNET, "mainnet" },
4140 { cryptonote::TESTNET, "testnet" },
4141 { cryptonote::STAGENET, "stagenet" },
4142 };
4143 if (!req.any_net_type && !m_wallet) return not_open(er);
4144 for (const auto &net_type: net_types)
4145 {
4146 if (!req.any_net_type && (!m_wallet || net_type.type != m_wallet->nettype()))
4147 continue;
4148 if (req.allow_openalias)
4149 {
4150 std::string address;
4151 res.valid = get_account_address_from_str_or_url(info, net_type.type, req.address,
4152 [&er, &address](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
4153 if (!dnssec_valid)
4154 {
4155 er.message = std::string("Invalid DNSSEC for ") + url;
4156 return {};
4157 }
4158 if (addresses.empty())
4159 {
4160 er.message = std::string("No ETN address found at ") + url;
4161 return {};
4162 }
4163 address = addresses[0];
4164 return address;
4165 });
4166 if (res.valid)
4167 res.openalias_address = address;
4168 }
4169 else
4170 {
4171 res.valid = cryptonote::get_account_address_from_str(info, net_type.type, req.address);
4172 }
4173 if (res.valid)
4174 {
4175 res.integrated = info.has_payment_id;
4176 res.subaddress = info.is_subaddress;
4177 res.nettype = net_type.stype;
4178 return true;
4179 }
4180 }
4181
4183 er.message = std::string("Invalid address");
4184 return false;
4185 }
4186 //------------------------------------------------------------------------------------------------------------------------------
4187 bool wallet_rpc_server::on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx)
4188 {
4189 if (!m_wallet) return not_open(er);
4190 if (m_restricted)
4191 {
4193 er.message = "Command unavailable in restricted mode.";
4194 return false;
4195 }
4196
4197 std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints;
4198 ssl_allowed_fingerprints.reserve(req.ssl_allowed_fingerprints.size());
4199 for (const std::string &fp: req.ssl_allowed_fingerprints)
4200 {
4201 ssl_allowed_fingerprints.push_back({});
4202 std::vector<uint8_t> &v = ssl_allowed_fingerprints.back();
4203 for (auto c: fp)
4204 v.push_back(c);
4205 }
4206
4208 if (req.ssl_allow_any_cert)
4210 else if (!ssl_allowed_fingerprints.empty() || !req.ssl_ca_file.empty())
4211 ssl_options = epee::net_utils::ssl_options_t{std::move(ssl_allowed_fingerprints), std::move(req.ssl_ca_file)};
4212
4213 if (!epee::net_utils::ssl_support_from_string(ssl_options.support, req.ssl_support))
4214 {
4216 er.message = std::string("Invalid ssl support mode");
4217 return false;
4218 }
4219
4221 std::move(req.ssl_private_key_path), std::move(req.ssl_certificate_path)
4222 };
4223
4224 const bool verification_required =
4227
4228 if (verification_required && !ssl_options.has_strong_verification(boost::string_ref{}))
4229 {
4231 er.message = "SSL is enabled but no user certificate or fingerprints were provided";
4232 return false;
4233 }
4234
4235 if (!m_wallet->set_daemon(req.address, boost::none, req.trusted, std::move(ssl_options)))
4236 {
4238 er.message = std::string("Unable to set daemon");
4239 return false;
4240 }
4241 return true;
4242 }
4243 //------------------------------------------------------------------------------------------------------------------------------
4244 bool wallet_rpc_server::on_set_log_level(const wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::response& res, epee::json_rpc::error& er, const connection_context *ctx)
4245 {
4246 if (m_restricted)
4247 {
4249 er.message = "Command unavailable in restricted mode.";
4250 return false;
4251 }
4252
4253 if (req.level < 0 || req.level > 4)
4254 {
4256 er.message = "Error: log level not valid";
4257 return false;
4258 }
4259 mlog_set_log_level(req.level);
4260 return true;
4261 }
4262 //------------------------------------------------------------------------------------------------------------------------------
4263 bool wallet_rpc_server::on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
4264 {
4265 if (m_restricted)
4266 {
4268 er.message = "Command unavailable in restricted mode.";
4269 return false;
4270 }
4271
4272 mlog_set_log(req.categories.c_str());
4273 res.categories = mlog_get_categories();
4274 return true;
4275 }
4276 //------------------------------------------------------------------------------------------------------------------------------
4277 bool wallet_rpc_server::on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx)
4278 {
4279 res.version = WALLET_RPC_VERSION;
4280 return true;
4281 }
4282 //------------------------------------------------------------------------------------------------------------------------------
4283}
4284
4286{
4287private:
4288 const boost::program_options::variables_map& vm;
4289
4290 std::unique_ptr<tools::wallet_rpc_server> wrpc;
4291
4292public:
4293 t_daemon(boost::program_options::variables_map const & _vm)
4294 : vm(_vm)
4295 , wrpc(new tools::wallet_rpc_server)
4296 {
4297 }
4298
4299 bool run()
4300 {
4301 std::unique_ptr<tools::wallet2> wal;
4302 try
4303 {
4304 const bool testnet = tools::wallet2::has_testnet_option(vm);
4305 const bool stagenet = tools::wallet2::has_stagenet_option(vm);
4306 if (testnet && stagenet)
4307 {
4308 MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet"));
4309 return false;
4310 }
4311
4312 const auto arg_wallet_file = wallet_args::arg_wallet_file();
4313 const auto arg_from_json = wallet_args::arg_generate_from_json();
4314
4315 const auto wallet_file = command_line::get_arg(vm, arg_wallet_file);
4316 const auto from_json = command_line::get_arg(vm, arg_from_json);
4317 const auto wallet_dir = command_line::get_arg(vm, arg_wallet_dir);
4318 const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password);
4319 const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
4320
4321 if(!wallet_file.empty() && !from_json.empty())
4322 {
4323 LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json"));
4324 return false;
4325 }
4326
4327 if (!wallet_dir.empty())
4328 {
4329 wal = NULL;
4330 goto just_dir;
4331 }
4332
4333 if (wallet_file.empty() && from_json.empty())
4334 {
4335 LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir"));
4336 return false;
4337 }
4338
4339 LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
4340 if(!wallet_file.empty())
4341 {
4342 wal = tools::wallet2::make_from_file(vm, true, wallet_file, password_prompt).first;
4343 }
4344 else
4345 {
4346 try
4347 {
4348 auto rc = tools::wallet2::make_from_json(vm, true, from_json, password_prompt);
4349 wal = std::move(rc.first);
4350 }
4351 catch (const std::exception &e)
4352 {
4353 MERROR("Error creating wallet: " << e.what());
4354 return false;
4355 }
4356 }
4357 if (!wal)
4358 {
4359 return false;
4360 }
4361
4362 bool quit = false;
4363 tools::signal_handler::install([&wal, &quit](int) {
4364 assert(wal);
4365 quit = true;
4366 wal->stop();
4367 });
4368
4369 wal->refresh(wal->is_trusted_daemon());
4370 // if we ^C during potentially length load/refresh, there's no server loop yet
4371 if (quit)
4372 {
4373 MINFO(tools::wallet_rpc_server::tr("Saving wallet..."));
4374 wal->store();
4375 MINFO(tools::wallet_rpc_server::tr("Successfully saved"));
4376 return false;
4377 }
4378 MINFO(tools::wallet_rpc_server::tr("Successfully loaded"));
4379 }
4380 catch (const std::exception& e)
4381 {
4382 LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what());
4383 return false;
4384 }
4385 just_dir:
4386 if (wal) wrpc->set_wallet(wal.release());
4387 bool r = wrpc->init(&vm);
4388 CHECK_AND_ASSERT_MES(r, false, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server"));
4389 tools::signal_handler::install([this](int) {
4390 wrpc->send_stop_signal();
4391 });
4392 LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server"));
4393 try
4394 {
4395 wrpc->run();
4396 }
4397 catch (const std::exception &e)
4398 {
4399 LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what());
4400 return false;
4401 }
4402 LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server"));
4403 try
4404 {
4405 LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet..."));
4406 wrpc->stop();
4407 LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved"));
4408 }
4409 catch (const std::exception& e)
4410 {
4411 LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what());
4412 return false;
4413 }
4414 return true;
4415 }
4416
4417 void stop()
4418 {
4419 wrpc->send_stop_signal();
4420 }
4421};
4422
4423class t_executor final
4424{
4425public:
4426 static std::string const NAME;
4427
4429
4430 std::string const & name() const
4431 {
4432 return NAME;
4433 }
4434
4435 t_daemon create_daemon(boost::program_options::variables_map const & vm)
4436 {
4437 return t_daemon(vm);
4438 }
4439
4440 bool run_non_interactive(boost::program_options::variables_map const & vm)
4441 {
4442 return t_daemon(vm).run();
4443 }
4444
4445 bool run_interactive(boost::program_options::variables_map const & vm)
4446 {
4447 return t_daemon(vm).run();
4448 }
4449};
4450
4451std::string const t_executor::NAME = "Wallet RPC Daemon";
4452
4453int main(int argc, char** argv) {
4454 TRY_ENTRY();
4455
4456 namespace po = boost::program_options;
4457
4458 const auto arg_wallet_file = wallet_args::arg_wallet_file();
4459 const auto arg_from_json = wallet_args::arg_generate_from_json();
4460
4461 po::options_description hidden_options("Hidden");
4462
4463 po::options_description desc_params(wallet_args::tr("Wallet options"));
4464 tools::wallet2::init_options(desc_params);
4465 command_line::add_arg(desc_params, arg_rpc_bind_port);
4466 command_line::add_arg(desc_params, arg_disable_rpc_login);
4467 command_line::add_arg(desc_params, arg_restricted);
4469 command_line::add_arg(desc_params, arg_wallet_file);
4470 command_line::add_arg(desc_params, arg_from_json);
4471 command_line::add_arg(desc_params, arg_wallet_dir);
4472 command_line::add_arg(desc_params, arg_prompt_for_password);
4473
4474 daemonizer::init_options(hidden_options, desc_params);
4475 desc_params.add(hidden_options);
4476
4477 boost::optional<po::variables_map> vm;
4478 bool should_terminate = false;
4479 std::tie(vm, should_terminate) = wallet_args::main(
4480 argc, argv,
4481 "electroneum-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
4482 tools::wallet_rpc_server::tr("This is the RPC electroneum wallet. It needs to connect to a electroneum\ndaemon to work correctly."),
4483 desc_params,
4484 po::positional_options_description(),
4485 [](const std::string &s, bool emphasis){ epee::set_console_color(emphasis ? epee::console_color_white : epee::console_color_default, true); std::cout << s << std::endl; if (emphasis) epee::reset_console_color(); },
4486 "electroneum-wallet-rpc.log",
4487 true
4488 );
4489 if (!vm)
4490 {
4491 return 1;
4492 }
4493 if (should_terminate)
4494 {
4495 return 0;
4496 }
4497
4498 return daemonizer::daemonize(argc, const_cast<const char**>(argv), t_executor{}, *vm) ? 0 : 1;
4499 CATCH_ENTRY_L0("main", 1);
4500}
else if(0==res)
int main()
uint64_t height
uint8_t threshold
bool run(size_t threads_count, bool wait=true)
bool init(std::function< void(size_t, uint8_t *)> rng, const std::string &bind_port="0", const std::string &bind_ip="0.0.0.0", std::vector< std::string > access_control_origins=std::vector< std::string >(), boost::optional< net_utils::http::login > user=boost::none, net_utils::ssl_options_t ssl_options=net_utils::ssl_support_t::e_ssl_support_autodetect)
net_utils::boosted_tcp_server< net_utils::http::http_custom_handler< epee::net_utils::connection_context_base > > m_net_server
ssl_verification_t verification
Definition net_ssl.h:82
bool has_strong_verification(boost::string_ref host) const noexcept
\retrurn True if host can be verified using this configuration WITHOUT system "root" CAs.
Definition net_ssl.cpp:402
ssl_authentication_t auth
Definition net_ssl.h:80
bool hex_to_pod(T &pod) const
const char * data() const noexcept
size_t size() const noexcept
t_daemon(boost::program_options::variables_map const &_vm)
static std::string const NAME
std::string const & name() const
bool run_interactive(boost::program_options::variables_map const &vm)
bool run_non_interactive(boost::program_options::variables_map const &vm)
t_daemon create_daemon(boost::program_options::variables_map const &vm)
static boost::optional< password_container > prompt(bool verify, const char *mesage="Password", bool hide_input=true)
Definition password.cpp:253
static private_file create(std::string filename)
Definition util.cpp:125
static bool install(T t)
installs a signal handler
Definition util.h:164
static bool has_testnet_option(const boost::program_options::variables_map &vm)
Definition wallet2.cpp:1173
static std::pair< std::unique_ptr< wallet2 >, password_container > make_new(const boost::program_options::variables_map &vm, bool unattended, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
Definition wallet2.cpp:1250
BackgroundMiningSetupType setup_background_mining() const
Definition wallet2.h:1107
static std::pair< std::unique_ptr< wallet2 >, password_container > make_from_json(const boost::program_options::variables_map &vm, bool unattended, const std::string &json_file, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 if no errors.
Definition wallet2.cpp:1227
std::vector< transfer_details > transfer_container
Definition wallet2.h:449
static std::pair< std::unique_ptr< wallet2 >, password_container > make_from_file(const boost::program_options::variables_map &vm, bool unattended, const std::string &wallet_file, const std::function< boost::optional< password_container >(const char *, bool)> &password_prompter)
Uses stdin and stdout. Returns a wallet2 and password for wallet_file if no errors.
Definition wallet2.cpp:1233
static bool parse_short_payment_id(const std::string &payment_id_str, crypto::hash8 &payment_id)
Definition wallet2.cpp:5725
BackgroundMiningSetupType
Definition wallet2.h:231
@ BackgroundMiningMaybe
Definition wallet2.h:232
static bool has_stagenet_option(const boost::program_options::variables_map &vm)
Definition wallet2.cpp:1178
bool is_trusted_daemon() const
Definition wallet2.h:765
static void init_options(boost::program_options::options_description &desc_params)
Definition wallet2.cpp:1193
bool init(const boost::program_options::variables_map *vm)
static const char * tr(const char *str)
#define tr(x)
#define CORE_RPC_STATUS_OK
#define CRYPTONOTE_MAX_BLOCK_NUMBER
Mnemonic seed generation and wallet restoration from them.
void * memcpy(void *a, const void *b, size_t c)
const char * res
const char * key
const char * i18n_translate(const char *s, const std::string &context)
Definition i18n.cpp:323
#define AUTO_VAL_INIT(v)
#define MERROR(x)
Definition misc_log_ex.h:73
#define LOG_PRINT_L3(x)
#define MDEBUG(x)
Definition misc_log_ex.h:76
std::string mlog_get_categories()
Definition mlog.cpp:276
void mlog_set_log(const char *log)
Definition mlog.cpp:288
void mlog_set_log_level(int level)
Definition mlog.cpp:282
#define CATCH_ENTRY_L0(lacation, return_val)
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define LOG_ERROR(x)
Definition misc_log_ex.h:98
#define MINFO(x)
Definition misc_log_ex.h:75
#define LOG_PRINT_L2(x)
#define TRY_ENTRY()
#define LOG_PRINT_L0(x)
Definition misc_log_ex.h:99
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
std::vector< const Language::Base * > get_language_list()
bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string &dst, size_t len, bool duplicate, std::string &language_name)
Converts seed words to bytes (secret key).
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
const std::string old_language_name
bool bytes_to_words(const char *src, size_t len, epee::wipeable_string &words, const std::string &language_name)
Converts bytes (secret key) to seed words.
std::enable_if< std::is_pod< T >::value, T >::type rand()
Definition crypto.h:216
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
Definition crypto.h:82
POD_CLASS hash8
Definition hash.h:53
POD_CLASS key_image
Definition crypto.h:105
POD_CLASS hash
Definition hash.h:50
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
crypto::hash get_transaction_hash(const transaction &t)
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
blobdata tx_to_blob(const transaction &tx)
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
std::string blobdata
std::string print_etn(uint64_t amount, unsigned int decimal_point)
bool get_account_address_from_str_or_url(address_parse_info &info, network_type nettype, const std::string &str_or_url, std::function< std::string(const std::string &, const std::vector< std::string > &, bool)> dns_confirm)
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
bool daemonize(int argc, char const *argv[], T_executor &&executor, boost::program_options::variables_map const &vm)
void init_options(boost::program_options::options_description &hidden_options, boost::program_options::options_description &normal_options)
@ none
Do not verify peer.
Definition net_ssl.h:54
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
Definition net_ssl.cpp:516
std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len)
bool parse_hexstr_to_binbuff(const epee::span< const char > s, epee::span< char > &res)
std::string pod_to_hex(const t_pod_type &s)
bool hex_to_pod(const std::string &hex_str, t_pod_type &s)
std::string buff_to_hex_nodelimer(const std::string &src)
void set_console_color(int color, bool bright)
Definition mlog.cpp:341
void reset_console_color()
Definition mlog.cpp:460
T & unwrap(mlocked< T > &src)
Definition mlocker.h:80
@ console_color_default
@ console_color_white
error
Tracks LMDB error codes.
Definition error.h:45
Various Tools.
Definition tools.cpp:31
unsigned get_max_concurrency()
Definition util.cpp:868
const char * tr(const char *str)
std::pair< boost::optional< boost::program_options::variables_map >, bool > main(int argc, char **argv, const char *const usage, const char *const notice, boost::program_options::options_description desc_params, const boost::program_options::positional_options_description &positional_options, const std::function< void(const std::string &, bool)> &print, const char *default_log_name, bool log_to_console)
command_line::arg_descriptor< std::string > arg_generate_from_json()
command_line::arg_descriptor< std::string > arg_wallet_file()
#define true
#define false
CXA_THROW_INFO_T void(* dest)(void *))
CXA_THROW_INFO_T * info
unsigned int uint32_t
Definition stdint.h:126
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
const command_line::arg_descriptor< std::string > rpc_login
Definition rpc_args.h:56
static boost::optional< rpc_args > process(const boost::program_options::variables_map &vm, const bool any_cert_option=false)
Definition rpc_args.cpp:125
static void init_options(boost::program_options::options_description &desc, const bool any_cert_option=false)
Definition rpc_args.cpp:108
bool is_subaddress
bool is_integrated
std::string original
uint64_t amount
account_public_address addr
static epee::wipeable_string wipeable_string(const span< const std::uint8_t > src)
Definition hex.cpp:69
static std::string string(const span< const std::uint8_t > src)
Definition hex.cpp:68
std::vector< cryptonote::tx_destination_entry > m_dests
Definition wallet2.h:400
std::unordered_set< crypto::public_key > m_signers
Definition wallet2.h:513
std::vector< pending_tx > m_ptx
Definition wallet2.h:512
cryptonote::subaddress_index m_subaddr_index
Definition wallet2.h:362
crypto::secret_key tx_key
Definition wallet2.h:472
std::vector< cryptonote::tx_destination_entry > dests
Definition wallet2.h:474
cryptonote::transaction tx
Definition wallet2.h:466
std::vector< crypto::secret_key > additional_tx_keys
Definition wallet2.h:473
std::vector< uint8_t > extra
Definition wallet2.h:426
std::vector< cryptonote::tx_source_entry > sources
Definition wallet2.h:422
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition wallet2.h:424
cryptonote::tx_destination_entry change_dts
Definition wallet2.h:423
enum tools::wallet2::unconfirmed_transfer_details::@320170243027143365014242201170062371014272002365 m_state
cryptonote::transaction_prefix m_tx
Definition wallet2.h:380
std::vector< cryptonote::tx_destination_entry > m_dests
Definition wallet2.h:385
std::vector< tx_construction_data > txes
Definition wallet2.h:499
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
std::string note
std::string payment_id
bool double_spend_seen
std::string type
std::string txid
uint64_t height
uint64_t unlock_time
bool nonexistent_utxo_seen
uint64_t timestamp
uint64_t amount
cryptonote::subaddress_index subaddr_index
std::string address
uint64_t fee
uint64_t confirmations
uint64_t suggested_confirmations_threshold
std::list< transfer_destination > destinations
std::vector< cryptonote::subaddress_index > subaddr_indices
const char * address
Definition multisig.cpp:37
const char * spendkey
Definition multisig.cpp:38
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
#define MKDIR(path, mode)
#define DEFAULT_AUTO_REFRESH_PERIOD
#define WALLET_RPC_VERSION
#define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC
#define WALLET_RPC_ERROR_CODE_TX_TOO_LARGE
#define WALLET_RPC_ERROR_CODE_BAD_HEX
#define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION
#define WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE
#define WALLET_RPC_ERROR_CODE_TRANSFER_TYPE
#define WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED
#define WALLET_RPC_ERROR_CODE_WATCH_ONLY
#define WALLET_RPC_ERROR_CODE_BAD_TX_METADATA
#define WALLET_RPC_ERROR_CODE_INVALID_PASSWORD
#define WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS
#define WALLET_RPC_ERROR_CODE_MULTISIG_SIGNATURE
#define WALLET_RPC_ERROR_CODE_WRONG_INDEX
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_UNLOCKED_ETN
#define WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID
#define WALLET_RPC_ERROR_CODE_NO_TXKEY
#define WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION
#define WALLET_RPC_ERROR_CODE_NOT_MULTISIG
#define WALLET_RPC_ERROR_CODE_ZERO_DESTINATION
#define WALLET_RPC_ERROR_CODE_NOT_OPEN
#define WALLET_RPC_ERROR_CODE_INVALID_LOG_LEVEL
#define WALLET_RPC_ERROR_CODE_WRONG_TXID
#define WALLET_RPC_ERROR_CODE_WRONG_KEY
#define WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE
#define WALLET_RPC_ERROR_CODE_WRONG_ADDRESS
#define WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUT_OF_BOUNDS
#define WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA
#define WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY
#define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR
#define WALLET_RPC_ERROR_CODE_NO_WALLET_DIR
#define WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA
#define WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS
#define WALLET_RPC_ERROR_CODE_DENIED
#define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_ETN
#define WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_OUTS_TO_MIX
#define WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG
#define WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA
#define WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR
#define WALLET_RPC_ERROR_CODE_WRONG_URI
#define WALLET_RPC_ERROR_CODE_MULTISIG_SUBMISSION
#define T(x)