Electroneum
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"
38 using 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"
56 #include "daemonizer/daemonizer.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 
63 namespace
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 
101 namespace 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 
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";
337  entry.subaddr_index = pd.m_subaddr_index;
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";
420  entry.subaddr_index = pd.m_subaddr_index;
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);
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  {
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 
1070  cryptonote::blobdata blob;
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 
1078  tools::wallet2::unsigned_tx_set exported_txs;
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  {
1089  tools::wallet2::signed_tx_set signed_txs;
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 {
1160  tools::wallet2::unsigned_tx_set exported_txs;
1161  cryptonote::blobdata blob;
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 {
1181  tools::wallet2::multisig_tx_set exported_txs;
1182  cryptonote::blobdata blob;
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  {
1250  const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d];
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 
1343  cryptonote::blobdata blob;
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);
1374  res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
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 
1500  crypto::key_image ki;
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 
1555  cryptonote::blobdata blob;
1556  if (!epee::string_tools::parse_hexstr_to_binbuff(req.hex, blob))
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  {
1904  epee::wipeable_string seed;
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  {
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  }
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) {
2499  const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
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 
2679  cryptonote::blobdata blob;
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);
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);
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;
3082  crypto::ElectrumWords::get_language_list(languages, false);
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  }
3286  catch (const tools::error::no_connection_to_daemon& e)
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  }
3306  catch (const tools::error::not_enough_unlocked_etn& e)
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)")) %
3316  cryptonote::print_etn(e.tx_amount() + e.fee()) %
3318  cryptonote::print_etn(e.fee())).str();
3319  er.message = e.what();
3320  }
3321  catch (const tools::error::not_enough_outs_to_mix& e)
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  {
3862  if (!epee::string_tools::parse_hexstr_to_binbuff(req.info[n], info[n]))
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 
4024  cryptonote::blobdata blob;
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 
4093  cryptonote::blobdata blob;
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  {
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 {
4287 private:
4288  const boost::program_options::variables_map& vm;
4289 
4290  std::unique_ptr<tools::wallet_rpc_server> wrpc;
4291 
4292 public:
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 
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 
4423 class t_executor final
4424 {
4425 public:
4426  static std::string const NAME;
4427 
4428  typedef ::t_daemon t_daemon;
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 
4451 std::string const t_executor::NAME = "Wallet RPC Daemon";
4452 
4453 int main(int argc, char** argv) {
4454  TRY_ENTRY();
4455 
4456  namespace po = boost::program_options;
4457 
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 }
const char * res
Definition: hmac_keccak.cpp:41
uint64_t balance_all(bool public_blockchain) const
Definition: wallet2.cpp:6292
bool watch_only() const
Definition: wallet2.h:825
std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len)
epee::misc_utils::struct_init< response_t > response
#define WALLET_RPC_ERROR_CODE_NOT_MULTISIG
static bool parse_long_payment_id(const std::string &payment_id_str, crypto::hash &payment_id)
Definition: wallet2.cpp:5712
void rescan_spent()
Definition: wallet2.cpp:6417
#define tr(x)
Definition: common_defines.h:4
std::FILE * handle() const noexcept
Definition: util.h:91
epee::misc_utils::struct_init< request_t > request
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
#define MERROR(x)
Definition: misc_log_ex.h:73
std::string get_reserve_proof(const boost::optional< std::pair< uint32_t, uint64_t >> &account_minreserve, const std::string &message)
Generates a proof that proves the reserve of unspent funds.
Definition: wallet2.cpp:11669
void get_unconfirmed_payments(std::list< std::pair< crypto::hash, wallet2::pool_payment_details >> &unconfirmed_payments, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition: wallet2.cpp:6408
cryptonote::transaction tx
Definition: wallet2.h:466
epee::misc_utils::struct_init< response_t > response
int main(int argc, char **argv)
bool sign_multisig_tx(multisig_tx_set &exported_txs, std::vector< crypto::hash > &txids)
Definition: wallet2.cpp:7607
epee::misc_utils::struct_init< request_t > request
#define LOG_PRINT_L2(x)
Definition: misc_log_ex.h:101
std::list< transfer_destination > destinations
const uint32_t T[512]
epee::misc_utils::struct_init< request_t > request
size_t get_num_subaddresses(uint32_t index_major) const
Definition: wallet2.h:802
net_utils::boosted_tcp_server< net_utils::http::http_custom_handler< epee::net_utils::connection_context_base > > m_net_server
#define WALLET_RPC_ERROR_CODE_NOT_OPEN
#define MKDIR(path, mode)
void get_payments(const crypto::hash &payment_id, std::list< wallet2::payment_details > &payments, uint64_t min_height=0, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition: wallet2.cpp:6320
uint64_t unlocked_balance(uint32_t subaddr_index_major, bool public_blockchain, uint64_t *blocks_to_unlock=NULL) const
Definition: wallet2.cpp:6172
cryptonote::blobdata export_multisig()
Definition: wallet2.cpp:12994
bool invoke_http_json(const boost::string_ref uri, const t_request &req, t_response &res, std::chrono::milliseconds timeout=std::chrono::seconds(15), const boost::string_ref http_method="GET")
Definition: wallet2.h:1319
bool init(const boost::program_options::variables_map *vm)
bool get_seed(epee::wipeable_string &electrum_words, const epee::wipeable_string &passphrase=epee::wipeable_string()) const
Definition: wallet2.cpp:1301
#define WALLET_RPC_ERROR_CODE_WRONG_URI
uint64_t unlock_time
epee::misc_utils::struct_init< request_t > request
bool key_on_device() const
Definition: wallet2.h:830
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
command_line::arg_descriptor< std::string > arg_wallet_file()
Definition: wallet_args.cpp:76
void init_options(boost::program_options::options_description &hidden_options, boost::program_options::options_description &normal_options)
uint64_t confirmations
size_t size() const noexcept
std::vector< const Language::Base * > get_language_list()
#define WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR
#define MINFO(x)
Definition: misc_log_ex.h:75
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
Definition: net_ssl.cpp:516
void commit_tx(pending_tx &ptx_vector)
Definition: wallet2.cpp:6887
epee::misc_utils::struct_init< response_t > response
cryptonote::subaddress_index m_subaddr_index
Definition: wallet2.h:362
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
#define WALLET_RPC_ERROR_CODE_MULTISIG_SUBMISSION
epee::misc_utils::struct_init< response_t > response
#define WALLET_RPC_ERROR_CODE_NO_WALLET_DIR
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
enum tools::wallet2::unconfirmed_transfer_details::@63 m_state
std::vector< crypto::secret_key > additional_tx_keys
Definition: wallet2.h:473
std::vector< cryptonote::tx_destination_entry > dests
Definition: wallet2.h:474
size_t import_multisig(std::vector< cryptonote::blobdata > info)
Definition: wallet2.cpp:13067
uint64_t fee
::std::string string
Definition: gtest-port.h:1097
CXA_THROW_INFO_T void(* dest)(void *))
Definition: stack_trace.cpp:91
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
std::string print_etn(uint64_t amount, unsigned int decimal_point)
bool is_transfer_unlocked(const transfer_details &td) const
Definition: wallet2.cpp:6565
uint64_t balance(uint32_t subaddr_index_major, bool public_blockchain) const
Definition: wallet2.cpp:6162
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
Definition: misc_log_ex.h:181
#define CRYPTONOTE_MAX_BLOCK_NUMBER
bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector< std::string > &unknown_parameters, std::string &error)
Definition: wallet2.cpp:13309
const std::string old_language_name
void mlog_set_log(const char *log)
Definition: mlog.cpp:288
epee::misc_utils::struct_init< request_t > request
std::string sign(const std::string &data) const
Definition: wallet2.cpp:12076
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
uint64_t timestamp
uint64_t height
Definition: blockchain.cpp:91
epee::misc_utils::struct_init< response_t > response
void stop()
Definition: wallet2.h:760
std::string exchange_multisig_keys(const epee::wipeable_string &password, const std::vector< std::string > &info)
Definition: wallet2.cpp:5280
std::vector< cryptonote::subaddress_index > subaddr_indices
bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function< bool(const multisig_tx_set &)> accept_func=NULL)
Definition: wallet2.cpp:7549
static boost::optional< rpc_args > process(const boost::program_options::variables_map &vm, const bool any_cert_option=false)
Definition: rpc_args.cpp:125
epee::misc_utils::struct_init< response_t > response
std::vector< wallet2::pending_tx > create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector< uint8_t > &extra, uint32_t subaddr_account, std::set< uint32_t > subaddr_indices, const bool migrate=false)
Definition: wallet2.cpp:10359
epee::misc_utils::struct_init< response_t > response
std::string make_multisig(const epee::wipeable_string &password, const std::vector< std::string > &info, uint32_t threshold)
Creates a multisig wallet.
Definition: wallet2.cpp:5466
bool double_spend_seen
const char * key
Definition: hmac_keccak.cpp:39
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
epee::misc_utils::struct_init< response_t > response
cryptonote::tx_destination_entry change_dts
Definition: wallet2.h:423
#define LOG_PRINT_L0(x)
Definition: misc_log_ex.h:99
uint64_t height
#define WALLET_RPC_ERROR_CODE_INVALID_PASSWORD
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
#define WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
std::vector< cryptonote::tx_destination_entry > m_dests
Definition: wallet2.h:400
epee::misc_utils::struct_init< response_t > response
cryptonote::subaddress_index subaddr_index
const char * tr(const char *str)
Definition: wallet_args.cpp:81
void change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
Definition: wallet2.cpp:4320
epee::misc_utils::struct_init< request_t > request
std::string get_spend_proof(const crypto::hash &txid, const std::string &message)
Definition: wallet2.cpp:11047
epee::misc_utils::struct_init< request_t > request
void get_unconfirmed_payments_out(std::list< std::pair< crypto::hash, wallet2::unconfirmed_transfer_details >> &unconfirmed_payments, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition: wallet2.cpp:6396
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).
unsigned char uint8_t
Definition: stdint.h:124
cryptonote::network_type nettype() const
Definition: wallet2.h:824
epee::misc_utils::struct_init< response_t > response
boost::optional< cryptonote::subaddress_index > get_subaddress_index(const cryptonote::account_public_address &address) const
Definition: wallet2.cpp:1437
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
Definition: protocol.cpp:1038
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
size_t import_outputs_from_str(const std::string &outputs_st)
Definition: wallet2.cpp:12828
std::string get_public_address_str(network_type nettype) const
Definition: account.cpp:269
void add_subaddress_account(const std::string &label, const bool update_account_tags=true)
Definition: wallet2.cpp:1463
boost::filesystem::path data_dir
Definition: main.cpp:50
#define WALLET_RPC_ERROR_CODE_WRONG_ADDRESS
epee::misc_utils::struct_init< request_t > request
#define DEFAULT_AUTO_REFRESH_PERIOD
std::unordered_set< crypto::public_key > m_signers
Definition: wallet2.h:513
void get_payments_out(std::list< std::pair< crypto::hash, wallet2::confirmed_transfer_details >> &confirmed_payments, uint64_t min_height, uint64_t max_height=(uint64_t) -1, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition: wallet2.cpp:6346
epee::misc_utils::struct_init< response_t > response
const char * i18n_translate(const char *s, const std::string &context)
Definition: i18n.cpp:323
#define WALLET_RPC_ERROR_CODE_INVALID_LOG_LEVEL
std::string note
epee::misc_utils::struct_init< response_t > response
void set_attribute(const std::string &key, const std::string &value)
Definition: wallet2.cpp:12013
std::string get_tx_note(const crypto::hash &txid) const
Definition: wallet2.cpp:11992
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
t_daemon(boost::program_options::variables_map const &_vm)
epee::misc_utils::struct_init< response_t > response
static void init_options(boost::program_options::options_description &desc, const bool any_cert_option=false)
Definition: rpc_args.cpp:108
static epee::wipeable_string wipeable_string(const span< const std::uint8_t > src)
Definition: hex.cpp:69
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)
epee::misc_utils::struct_init< request_t > request
void set_seed_language(const std::string &language)
Sets the seed language.
Definition: wallet2.cpp:1425
BackgroundMiningSetupType
Definition: wallet2.h:231
bool hex_to_pod(T &pod) const
std::string txid
epee::misc_utils::struct_init< response_t > response
#define CORE_RPC_STATUS_OK
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
uint64_t import_key_images(const std::vector< std::pair< crypto::key_image, crypto::signature >> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent=true)
Definition: wallet2.cpp:12333
static boost::optional< password_container > prompt(bool verify, const char *mesage="Password", bool hide_input=true)
Definition: password.cpp:253
epee::misc_utils::struct_init< request_t > request
std::vector< cryptonote::tx_destination_entry > m_dests
Definition: wallet2.h:385
std::vector< cryptonote::tx_source_entry > sources
Definition: wallet2.h:422
#define WALLET_RPC_VERSION
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
#define MDEBUG(x)
Definition: misc_log_ex.h:76
std::map< uint32_t, uint64_t > balance_per_subaddress(uint32_t subaddr_index_major, bool public_blockchain=false) const
Definition: wallet2.cpp:6188
const account_keys & get_keys() const
Definition: account.cpp:264
unsigned get_max_concurrency()
Definition: util.cpp:868
#define WALLET_RPC_ERROR_CODE_MULTISIG_SIGNATURE
#define WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY
epee::misc_utils::struct_init< request_t > request
bool has_multisig_partial_key_images() const
Definition: wallet2.cpp:5647
std::vector< tx_construction_data > txes
Definition: wallet2.h:499
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
void reset_console_color()
Definition: mlog.cpp:460
epee::misc_utils::struct_init< request_t > request
return true
epee::misc_utils::struct_init< request_t > request
std::vector< wallet2::pending_tx > create_transactions_2(std::vector< cryptonote::tx_destination_entry > dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector< uint8_t > &extra, uint32_t subaddr_account, std::set< uint32_t > subaddr_indices)
Definition: wallet2.cpp:9725
blobdata tx_to_blob(const transaction &tx)
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
std::string pod_to_hex(const t_pod_type &s)
Definition: string_tools.h:317
void get_payments_out_migration(std::list< std::pair< crypto::hash, wallet2::confirmed_transfer_details >> &confirmed_payments, uint64_t min_height, uint64_t max_height=(uint64_t) -1, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition: wallet2.cpp:6363
void rand(size_t N, uint8_t *bytes)
Definition: crypto.h:209
bool export_key_images(const std::string &filename) const
Definition: wallet2.cpp:12191
std::vector< transfer_details > transfer_container
Definition: wallet2.h:449
epee::misc_utils::struct_init< response_t > response
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.
bool finalize_multisig(const epee::wipeable_string &password, const std::vector< std::string > &info)
Finalizes creation of a multisig wallet.
Definition: wallet2.cpp:5516
std::string address
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
#define TRY_ENTRY()
Definition: misc_log_ex.h:151
unsigned int uint32_t
Definition: stdint.h:126
uint64_t suggested_confirmations_threshold
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
Various Tools.
Definition: tools.cpp:31
std::string mlog_get_categories()
Definition: mlog.cpp:276
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
void set_tx_note(const crypto::hash &txid, const std::string &note)
Definition: wallet2.cpp:11987
epee::misc_utils::struct_init< request_t > request
bool is_trusted_daemon() const
Definition: wallet2.h:765
#define WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS
#define WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA
ssl_authentication_t auth
Definition: net_ssl.h:80
epee::misc_utils::struct_init< response_t > response
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)
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
static bool verify_password(const std::string &keys_file_name, const epee::wipeable_string &password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds)
verify password for specified wallet keys file.
Definition: wallet2.cpp:4699
std::string export_outputs_to_str(bool all=false) const
Definition: wallet2.cpp:12744
epee::misc_utils::struct_init< response_t > response
#define WALLET_RPC_ERROR_CODE_BAD_HEX
epee::misc_utils::struct_init< request_t > request
unsigned __int64 uint64_t
Definition: stdint.h:136
bool daemonize(int argc, char const *argv[], T_executor &&executor, boost::program_options::variables_map const &vm)
void get_payments_out_sc_migration(std::list< std::pair< crypto::hash, wallet2::confirmed_transfer_details >> &confirmed_payments, uint64_t min_height, uint64_t max_height=(uint64_t) -1, const boost::optional< uint32_t > &subaddr_account=boost::none, const std::set< uint32_t > &subaddr_indices={}) const
Definition: wallet2.cpp:6380
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition: wallet2.h:424
const command_line::arg_descriptor< std::string, false, true, 2 > arg_data_dir
std::string get_subaddress_as_str(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1452
std::string sign_tx_dump_to_str(unsigned_tx_set &exported_txs, std::vector< wallet2::pending_tx > &ptx, signed_tx_set &signed_txes)
Definition: wallet2.cpp:7295
epee::misc_utils::struct_init< response_t > response
#define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED
epee::misc_utils::struct_init< response_t > response
command_line::arg_descriptor< std::string > arg_generate_from_json()
Definition: wallet_args.cpp:72
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
bool run_interactive(boost::program_options::variables_map const &vm)
bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress)
Definition: wallet2.cpp:3451
uint64_t amount
bool hex_to_pod(const std::string &hex_str, t_pod_type &s)
Definition: string_tools.h:324
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector< tools::wallet2::pending_tx > &ptx, std::function< bool(const signed_tx_set &)> accept_func)
Definition: wallet2.cpp:7342
#define false
Definition: stdbool.h:38
epee::misc_utils::struct_init< response_t > response
std::string get_wallet_file() const
Definition: wallet2.cpp:11916
static void init_options(boost::program_options::options_description &desc_params)
Definition: wallet2.cpp:1193
bool is_subaddress
std::vector< uint8_t > extra
Definition: wallet2.h:426
#define WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA
bool is_integrated
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
#define WALLET_RPC_ERROR_CODE_WATCH_ONLY
epee::misc_utils::struct_init< response_t > response
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector< crypto::secret_key > &additional_tx_keys)
Definition: wallet2.cpp:10924
epee::misc_utils::struct_init< request_t > request
#define LOG_PRINT_L3(x)
Definition: misc_log_ex.h:102
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_OUTS_TO_MIX
epee::misc_utils::struct_init< response_t > response
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
Definition: command_line.h:188
static bool parse_short_payment_id(const std::string &payment_id_str, crypto::hash8 &payment_id)
Definition: wallet2.cpp:5725
void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label)
Definition: wallet2.cpp:1528
#define WALLET_RPC_ERROR_CODE_WRONG_INDEX
void set_account_tag_description(const std::string &tag, const std::string &description)
Set the label of the given tag.
Definition: wallet2.cpp:12069
#define WALLET_RPC_ERROR_CODE_ZERO_DESTINATION
#define WALLET_RPC_ERROR_CODE_DENIED
crypto::secret_key m_view_secret_key
Definition: account.h:45
void set_refresh_from_block_height(uint64_t height)
Definition: wallet2.h:742
bool run(size_t threads_count, bool wait=true)
void set_console_color(int color, bool bright)
Definition: mlog.cpp:341
std::string get_multisig_info() const
Definition: wallet2.cpp:5529
static bool install(T t)
installs a signal handler
Definition: util.h:164
epee::misc_utils::struct_init< request_t > request
bool check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations)
Definition: wallet2.cpp:11527
account_public_address addr
cryptonote::transaction_prefix m_tx
Definition: wallet2.h:380
std::string blobdata
Definition: blobdatatype.h:39
epee::misc_utils::struct_init< request_t > request
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
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
#define WALLET_RPC_ERROR_CODE_BAD_TX_METADATA
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
epee::misc_utils::struct_init< request_t > request
bool is_deterministic() const
Checks if deterministic wallet.
Definition: wallet2.cpp:1293
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)
Definition: wallet_args.cpp:86
bool check_reserve_proof(const cryptonote::account_public_address &address, const std::string &message, const std::string &sig_str, uint64_t &total, uint64_t &spent)
Verifies a proof of reserve.
Definition: wallet2.cpp:11790
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
size_t get_num_subaddress_accounts() const
Definition: wallet2.h:801
POD_CLASS hash8
Definition: hash.h:53
uint64_t amount
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
const T & move(const T &t)
Definition: gtest-port.h:1317
bool synced_to_v10() const
Definition: wallet2.h:899
epee::misc_utils::struct_init< request_t > request
t_daemon create_daemon(boost::program_options::variables_map const &vm)
std::string original
epee::misc_utils::struct_init< request_t > request
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const
Definition: wallet2.cpp:13255
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
#define WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const
Definition: wallet2.cpp:7033
std::string buff_to_hex_nodelimer(const std::string &src)
Definition: string_tools.h:87
epee::misc_utils::struct_init< response_t > response
POD_CLASS key_image
Definition: crypto.h:102
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
epee::misc_utils::struct_init< response_t > response
void * memcpy(void *a, const void *b, size_t c)
crypto::secret_key m_spend_secret_key
Definition: account.h:44
static const char * tr(const char *str)
static bool has_testnet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1173
std::string dump_tx_to_str(const std::vector< pending_tx > &ptx_vector) const
Definition: wallet2.cpp:6984
void set_account_tag(const std::set< uint32_t > &account_indices, const std::string &tag)
Set a tag to the given accounts.
Definition: wallet2.cpp:12056
::t_daemon t_daemon
bool check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str)
Definition: wallet2.cpp:11163
crypto::hash get_transaction_hash(const transaction &t)
epee::misc_utils::struct_init< request_t > request
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
boost::program_options::basic_parsed_options< charT > parse_command_line(int argc, const charT *const argv[], const boost::program_options::options_description &desc, bool allow_unregistered=false)
Definition: command_line.h:224
bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const
Definition: wallet2.cpp:12086
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
epee::misc_utils::struct_init< response_t > response
ssl_verification_t verification
Definition: net_ssl.h:82
#define WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUT_OF_BOUNDS
static bool has_stagenet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1178
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_ETN
Mnemonic seed generation and wallet restoration from them.
epee::misc_utils::struct_init< response_t > response
T & unwrap(mlocked< T > &src)
Definition: mlocker.h:80
bool nonexistent_utxo_seen
#define AUTO_VAL_INIT(v)
Definition: misc_language.h:53
std::vector< address_book_row > get_address_book() const
GUI Address book get/store.
Definition: wallet2.h:1150
static std::string string(const span< const std::uint8_t > src)
Definition: hex.cpp:68
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
bool has_strong_verification(boost::string_ref host) const noexcept
True if host can be verified using this configuration WITHOUT system "root" CAs.
Definition: net_ssl.cpp:402
static private_file create(std::string filename)
Definition: util.cpp:125
std::string save_multisig_tx(multisig_tx_set txs)
Definition: wallet2.cpp:7431
epee::misc_utils::struct_init< response_t > response
std::set< uint32_t > m_subaddr_indices
Definition: wallet2.h:405
void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector< crypto::secret_key > &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
Definition: wallet2.cpp:11276
#define WALLET_RPC_ERROR_CODE_TX_TOO_LARGE
#define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
bool delete_address_book_row(std::size_t row_id)
Definition: wallet2.cpp:3466
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
#define WALLET_RPC_ERROR_CODE_TRANSFER_TYPE
cryptonote::account_base & get_account()
Definition: wallet2.h:734
POD_CLASS hash
Definition: hash.h:50
#define WALLET_RPC_ERROR_CODE_NO_TXKEY
uint64_t get_blockchain_current_height() const
Definition: wallet2.h:898
std::string get_integrated_address_as_str(const crypto::hash8 &payment_id) const
Definition: wallet2.cpp:1458
#define WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE
epee::misc_utils::struct_init< request_t > request
void account_major_offset(uint32_t offset)
Definition: wallet2.h:810
epee::misc_utils::struct_init< response_t > response
void rescan_blockchain(bool hard, bool refresh=true, bool keep_key_images=false)
Definition: wallet2.cpp:6540
crypto::secret_key tx_key
Definition: wallet2.h:472
epee::misc_utils::struct_init< response_t > response
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_UNLOCKED_ETN
const char * address
Definition: multisig.cpp:37
const char * spendkey
Definition: multisig.cpp:38
std::string to_string(t_connection_type type)
std::vector< pending_tx > create_unmixable_sweep_transactions()
Definition: wallet2.cpp:10870
#define WALLET_RPC_ERROR_CODE_WRONG_TXID
#define WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
#define WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA
bool get_multisig_seed(epee::wipeable_string &seed, const epee::wipeable_string &passphrase=std::string(), bool raw=true) const
Definition: wallet2.cpp:1327
else if(0==res)
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
std::vector< wallet2::pending_tx > create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector< uint8_t > &extra)
Definition: wallet2.cpp:10416
void refresh(bool trusted_daemon)
Definition: wallet2.cpp:3060
std::vector< pending_tx > m_ptx
Definition: wallet2.h:512
const std::pair< std::map< std::string, std::string >, std::vector< std::string > > & get_account_tags()
Get the list of registered account tags.
Definition: wallet2.cpp:12036
epee::misc_utils::struct_init< response_t > response
#define WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
bool run_non_interactive(boost::program_options::variables_map const &vm)
const char * data() const noexcept
std::map< uint32_t, std::pair< uint64_t, uint64_t > > unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool public_blockchain=false) const
Definition: wallet2.cpp:6249
uint64_t adjust_mixin(uint64_t mixin) const
Definition: wallet2.cpp:7860
epee::misc_utils::struct_init< request_t > request
std::string type
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
std::string get_attribute(const std::string &key) const
Definition: wallet2.cpp:12018
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
uint64_t get_last_block_reward() const
Definition: wallet2.h:906
uint64_t unlocked_balance_all(bool public_blockchain, uint64_t *blocks_to_unlock=NULL) const
Definition: wallet2.cpp:6300
#define CATCH_ENTRY_L0(lacation, return_val)
Definition: misc_log_ex.h:165
uint32_t adjust_priority(uint32_t priority)
Definition: wallet2.cpp:7877
epee::misc_utils::struct_init< response_t > response
error
Tracks LMDB error codes.
Definition: error.h:44
file_error_base< file_exists_message_index > file_exists
void mlog_set_log_level(int level)
Definition: mlog.cpp:282
epee::misc_utils::struct_init< request_t > request
#define WALLET_RPC_ERROR_CODE_WRONG_KEY
bool parse_hexstr_to_binbuff(const epee::span< const char > s, epee::span< char > &res)
Definition: string_tools.h:92
#define WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID
std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message)
Definition: wallet2.cpp:11371
static std::string const NAME
#define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION
bool parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const
Definition: wallet2.cpp:7505
void generate(const std::string &wallet_, const epee::wipeable_string &password, const epee::wipeable_string &multisig_data, bool create_address_file=false)
Generates a wallet or restores one.
Definition: wallet2.cpp:4869
void update_pool_state(bool refreshed=false)
Definition: wallet2.cpp:3164
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
Definition: command_line.h:265
std::string const & name() const
void add_subaddress(uint32_t index_major, const std::string &label)
Definition: wallet2.cpp:1470
std::string payment_id
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
#define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC
epee::misc_utils::struct_init< request_t > request
BackgroundMiningSetupType setup_background_mining() const
Definition: wallet2.h:1107
epee::misc_utils::struct_init< request_t > request
#define WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS
std::string get_subaddress_label(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1518
#define WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG
epee::misc_utils::struct_init< response_t > response
bool multisig(bool *ready=NULL, uint32_t *threshold=NULL, uint32_t *total=NULL) const
Definition: wallet2.cpp:5634
void get_transfers(wallet2::transfer_container &incoming_transfers) const
Definition: wallet2.cpp:6315
uint8_t threshold
Definition: blockchain.cpp:92