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")
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" : "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];
1251  std::string address = cryptonote::get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr);
1252  if (has_encrypted_payment_id && !entry.is_subaddress && address != entry.original)
1253  address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8);
1254  auto i = dests.find(entry.addr);
1255  if (i == dests.end())
1256  dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
1257  else
1258  i->second.second += entry.amount;
1259  desc.amount_out += entry.amount;
1260  }
1261  if (cd.change_dts.amount > 0)
1262  {
1263  auto it = dests.find(cd.change_dts.addr);
1264  if (it == dests.end())
1265  {
1267  er.message = "Claimed change does not go to a paid address";
1268  return false;
1269  }
1270  if (it->second.second < cd.change_dts.amount)
1271  {
1273  er.message = "Claimed change is larger than payment to the change address";
1274  return false;
1275  }
1276  if (cd.change_dts.amount > 0)
1277  {
1278  if (first_known_non_zero_change_index == -1)
1279  first_known_non_zero_change_index = n;
1280  const tools::wallet2::tx_construction_data &cdn = tx_constructions[first_known_non_zero_change_index];
1281  if (memcmp(&cd.change_dts.addr, &cdn.change_dts.addr, sizeof(cd.change_dts.addr)))
1282  {
1284  er.message = "Change goes to more than one address";
1285  return false;
1286  }
1287  }
1288  desc.change_amount += cd.change_dts.amount;
1289  it->second.second -= cd.change_dts.amount;
1290  if (it->second.second == 0)
1291  dests.erase(cd.change_dts.addr);
1292  }
1293 
1294  size_t n_dummy_outputs = 0;
1295  for (auto i = dests.begin(); i != dests.end(); )
1296  {
1297  if (i->second.second > 0)
1298  {
1299  desc.recipients.push_back({i->second.first, i->second.second});
1300  }
1301  else
1302  ++desc.dummy_outputs;
1303  ++i;
1304  }
1305 
1306  if (desc.change_amount > 0)
1307  {
1308  const tools::wallet2::tx_construction_data &cd0 = tx_constructions[0];
1309  desc.change_address = get_account_address_as_str(m_wallet->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr);
1310  }
1311 
1312  desc.fee = desc.amount_in - desc.amount_out;
1313  desc.unlock_time = cd.unlock_time;
1314  desc.extra = epee::to_hex::string({cd.extra.data(), cd.extra.size()});
1315  }
1316  }
1317  catch (const std::exception &e)
1318  {
1320  er.message = "failed to parse unsigned transfers";
1321  return false;
1322  }
1323 
1324  return true;
1325  }
1326  //------------------------------------------------------------------------------------------------------------------------------
1327  bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1328  {
1329  if (!m_wallet) return not_open(er);
1330  if (m_restricted)
1331  {
1333  er.message = "Command unavailable in restricted mode.";
1334  return false;
1335  }
1336  if (m_wallet->key_on_device())
1337  {
1339  er.message = "command not supported by HW wallet";
1340  return false;
1341  }
1342 
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  {
1946  epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_view_secret_key);
1947  res.key = std::string(key.data(), key.size());
1948  }
1949  else if(req.key_type.compare("spend_key") == 0)
1950  {
1951  if (m_wallet->watch_only())
1952  {
1954  er.message = "The wallet is watch-only. Cannot retrieve spend key.";
1955  return false;
1956  }
1957  epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
1958  res.key = std::string(key.data(), key.size());
1959  }
1960  else
1961  {
1962  er.message = "key_type " + req.key_type + " not found";
1963  return false;
1964  }
1965 
1966  return true;
1967  }
1968  //------------------------------------------------------------------------------------------------------------------------------
1969  bool wallet_rpc_server::on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1970  {
1971  if (!m_wallet) return not_open(er);
1972  if (m_restricted)
1973  {
1975  er.message = "Command unavailable in restricted mode.";
1976  return false;
1977  }
1978 
1979  try
1980  {
1981  m_wallet->rescan_blockchain(req.hard);
1982  }
1983  catch (const std::exception& e)
1984  {
1985  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
1986  return false;
1987  }
1988  return true;
1989  }
1990  //------------------------------------------------------------------------------------------------------------------------------
1991  bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er, const connection_context *ctx)
1992  {
1993  if (!m_wallet) return not_open(er);
1994  if (m_restricted)
1995  {
1997  er.message = "Command unavailable in restricted mode.";
1998  return false;
1999  }
2000 
2001  res.signature = m_wallet->sign(req.data);
2002  return true;
2003  }
2004  //------------------------------------------------------------------------------------------------------------------------------
2005  bool wallet_rpc_server::on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2006  {
2007  if (!m_wallet) return not_open(er);
2008  if (m_restricted)
2009  {
2011  er.message = "Command unavailable in restricted mode.";
2012  return false;
2013  }
2014 
2016  er.message = "";
2017  if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), req.address,
2018  [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
2019  if (!dnssec_valid)
2020  {
2021  er.message = std::string("Invalid DNSSEC for ") + url;
2022  return {};
2023  }
2024  if (addresses.empty())
2025  {
2026  er.message = std::string("No ETN address found at ") + url;
2027  return {};
2028  }
2029  return addresses[0];
2030  }))
2031  {
2033  return false;
2034  }
2035 
2036  res.good = m_wallet->verify(req.data, info.address, req.signature);
2037  return true;
2038  }
2039  //------------------------------------------------------------------------------------------------------------------------------
2040  bool wallet_rpc_server::on_stop_wallet(const wallet_rpc::COMMAND_RPC_STOP_WALLET::request& req, wallet_rpc::COMMAND_RPC_STOP_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2041  {
2042  if (!m_wallet) return not_open(er);
2043  if (m_restricted)
2044  {
2046  er.message = "Command unavailable in restricted mode.";
2047  return false;
2048  }
2049 
2050  try
2051  {
2052  m_wallet->store();
2053  m_stop.store(true, std::memory_order_relaxed);
2054  }
2055  catch (const std::exception& e)
2056  {
2057  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2058  return false;
2059  }
2060  return true;
2061  }
2062  //------------------------------------------------------------------------------------------------------------------------------
2063  bool wallet_rpc_server::on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2064  {
2065  if (!m_wallet) return not_open(er);
2066  if (m_restricted)
2067  {
2069  er.message = "Command unavailable in restricted mode.";
2070  return false;
2071  }
2072 
2073  if (req.txids.size() != req.notes.size())
2074  {
2076  er.message = "Different amount of txids and notes";
2077  return false;
2078  }
2079 
2080  std::list<crypto::hash> txids;
2081  std::list<std::string>::const_iterator i = req.txids.begin();
2082  while (i != req.txids.end())
2083  {
2084  cryptonote::blobdata txid_blob;
2085  if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash))
2086  {
2088  er.message = "TX ID has invalid format";
2089  return false;
2090  }
2091 
2092  crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
2093  txids.push_back(txid);
2094  }
2095 
2096  std::list<crypto::hash>::const_iterator il = txids.begin();
2097  std::list<std::string>::const_iterator in = req.notes.begin();
2098  while (il != txids.end())
2099  {
2100  m_wallet->set_tx_note(*il++, *in++);
2101  }
2102 
2103  return true;
2104  }
2105  //------------------------------------------------------------------------------------------------------------------------------
2106  bool wallet_rpc_server::on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2107  {
2108  res.notes.clear();
2109  if (!m_wallet) return not_open(er);
2110 
2111  std::list<crypto::hash> txids;
2112  std::list<std::string>::const_iterator i = req.txids.begin();
2113  while (i != req.txids.end())
2114  {
2115  cryptonote::blobdata txid_blob;
2116  if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash))
2117  {
2119  er.message = "TX ID has invalid format";
2120  return false;
2121  }
2122 
2123  crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
2124  txids.push_back(txid);
2125  }
2126 
2127  std::list<crypto::hash>::const_iterator il = txids.begin();
2128  while (il != txids.end())
2129  {
2130  res.notes.push_back(m_wallet->get_tx_note(*il++));
2131  }
2132  return true;
2133  }
2134  //------------------------------------------------------------------------------------------------------------------------------
2135  bool wallet_rpc_server::on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2136  {
2137  if (!m_wallet) return not_open(er);
2138  if (m_restricted)
2139  {
2141  er.message = "Command unavailable in restricted mode.";
2142  return false;
2143  }
2144 
2145  m_wallet->set_attribute(req.key, req.value);
2146 
2147  return true;
2148  }
2149  //------------------------------------------------------------------------------------------------------------------------------
2150  bool wallet_rpc_server::on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2151  {
2152  if (!m_wallet) return not_open(er);
2153  if (m_restricted)
2154  {
2156  er.message = "Command unavailable in restricted mode.";
2157  return false;
2158  }
2159 
2160  res.value = m_wallet->get_attribute(req.key);
2161  return true;
2162  }
2163  bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2164  {
2165  if (!m_wallet) return not_open(er);
2166 
2167  crypto::hash txid;
2168  if (!epee::string_tools::hex_to_pod(req.txid, txid))
2169  {
2171  er.message = "TX ID has invalid format";
2172  return false;
2173  }
2174 
2175  crypto::secret_key tx_key;
2176  std::vector<crypto::secret_key> additional_tx_keys;
2177  if (!m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
2178  {
2180  er.message = "No tx secret key is stored for this tx";
2181  return false;
2182  }
2183 
2185  s += epee::to_hex::wipeable_string(tx_key);
2186  for (size_t i = 0; i < additional_tx_keys.size(); ++i)
2187  s += epee::to_hex::wipeable_string(additional_tx_keys[i]);
2188  res.tx_key = std::string(s.data(), s.size());
2189  return true;
2190  }
2191  //------------------------------------------------------------------------------------------------------------------------------
2192  bool wallet_rpc_server::on_check_tx_key(const wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2193  {
2194  if (!m_wallet) return not_open(er);
2195 
2196  crypto::hash txid;
2197  if (!epee::string_tools::hex_to_pod(req.txid, txid))
2198  {
2200  er.message = "TX ID has invalid format";
2201  return false;
2202  }
2203 
2204  epee::wipeable_string tx_key_str = req.tx_key;
2205  if (tx_key_str.size() < 64 || tx_key_str.size() % 64)
2206  {
2208  er.message = "Tx key has invalid format";
2209  return false;
2210  }
2211  const char *data = tx_key_str.data();
2212  crypto::secret_key tx_key;
2213  if (!epee::wipeable_string(data, 64).hex_to_pod(unwrap(unwrap(tx_key))))
2214  {
2216  er.message = "Tx key has invalid format";
2217  return false;
2218  }
2219  size_t offset = 64;
2220  std::vector<crypto::secret_key> additional_tx_keys;
2221  while (offset < tx_key_str.size())
2222  {
2223  additional_tx_keys.resize(additional_tx_keys.size() + 1);
2224  if (!epee::wipeable_string(data + offset, 64).hex_to_pod(unwrap(unwrap(additional_tx_keys.back()))))
2225  {
2227  er.message = "Tx key has invalid format";
2228  return false;
2229  }
2230  offset += 64;
2231  }
2232 
2234  if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
2235  {
2237  er.message = "Invalid address";
2238  return false;
2239  }
2240 
2241  try
2242  {
2243  m_wallet->check_tx_key(txid, tx_key, additional_tx_keys, info.address, res.received, res.in_pool, res.confirmations);
2244  }
2245  catch (const std::exception &e)
2246  {
2248  er.message = e.what();
2249  return false;
2250  }
2251  return true;
2252  }
2253  //------------------------------------------------------------------------------------------------------------------------------
2254  bool wallet_rpc_server::on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2255  {
2256  if (!m_wallet) return not_open(er);
2257 
2258  crypto::hash txid;
2259  if (!epee::string_tools::hex_to_pod(req.txid, txid))
2260  {
2262  er.message = "TX ID has invalid format";
2263  return false;
2264  }
2265 
2267  if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
2268  {
2270  er.message = "Invalid address";
2271  return false;
2272  }
2273 
2274  try
2275  {
2276  res.signature = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, req.message);
2277  }
2278  catch (const std::exception &e)
2279  {
2281  er.message = e.what();
2282  return false;
2283  }
2284  return true;
2285  }
2286  //------------------------------------------------------------------------------------------------------------------------------
2287  bool wallet_rpc_server::on_check_tx_proof(const wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2288  {
2289  if (!m_wallet) return not_open(er);
2290 
2291  crypto::hash txid;
2292  if (!epee::string_tools::hex_to_pod(req.txid, txid))
2293  {
2295  er.message = "TX ID has invalid format";
2296  return false;
2297  }
2298 
2300  if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
2301  {
2303  er.message = "Invalid address";
2304  return false;
2305  }
2306 
2307  try
2308  {
2309  res.good = m_wallet->check_tx_proof(txid, info.address, info.is_subaddress, req.message, req.signature, res.received, res.in_pool, res.confirmations);
2310  }
2311  catch (const std::exception &e)
2312  {
2314  er.message = e.what();
2315  return false;
2316  }
2317  return true;
2318  }
2319  //------------------------------------------------------------------------------------------------------------------------------
2320  bool wallet_rpc_server::on_get_spend_proof(const wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2321  {
2322  if (!m_wallet) return not_open(er);
2323 
2324  crypto::hash txid;
2325  if (!epee::string_tools::hex_to_pod(req.txid, txid))
2326  {
2328  er.message = "TX ID has invalid format";
2329  return false;
2330  }
2331 
2332  try
2333  {
2334  res.signature = m_wallet->get_spend_proof(txid, req.message);
2335  }
2336  catch (const std::exception &e)
2337  {
2339  er.message = e.what();
2340  return false;
2341  }
2342  return true;
2343  }
2344  //------------------------------------------------------------------------------------------------------------------------------
2345  bool wallet_rpc_server::on_check_spend_proof(const wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2346  {
2347  if (!m_wallet) return not_open(er);
2348 
2349  crypto::hash txid;
2350  if (!epee::string_tools::hex_to_pod(req.txid, txid))
2351  {
2353  er.message = "TX ID has invalid format";
2354  return false;
2355  }
2356 
2357  try
2358  {
2359  res.good = m_wallet->check_spend_proof(txid, req.message, req.signature);
2360  }
2361  catch (const std::exception &e)
2362  {
2364  er.message = e.what();
2365  return false;
2366  }
2367  return true;
2368  }
2369  //------------------------------------------------------------------------------------------------------------------------------
2370  bool wallet_rpc_server::on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2371  {
2372  if (!m_wallet) return not_open(er);
2373 
2374  boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
2375  if (!req.all)
2376  {
2377  if (req.account_index >= m_wallet->get_num_subaddress_accounts())
2378  {
2380  er.message = "Account index is out of bound";
2381  return false;
2382  }
2383  account_minreserve = std::make_pair(req.account_index, req.amount);
2384  }
2385 
2386  try
2387  {
2388  res.signature = m_wallet->get_reserve_proof(account_minreserve, req.message);
2389  }
2390  catch (const std::exception &e)
2391  {
2393  er.message = e.what();
2394  return false;
2395  }
2396  return true;
2397  }
2398  //------------------------------------------------------------------------------------------------------------------------------
2399  bool wallet_rpc_server::on_check_reserve_proof(const wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2400  {
2401  if (!m_wallet) return not_open(er);
2402 
2404  if (!get_account_address_from_str(info, m_wallet->nettype(), req.address))
2405  {
2407  er.message = "Invalid address";
2408  return false;
2409  }
2410  if (info.is_subaddress)
2411  {
2413  er.message = "Address must not be a subaddress";
2414  return false;
2415  }
2416 
2417  try
2418  {
2419  res.good = m_wallet->check_reserve_proof(info.address, req.message, req.signature, res.total, res.spent);
2420  }
2421  catch (const std::exception &e)
2422  {
2424  er.message = e.what();
2425  return false;
2426  }
2427  return true;
2428  }
2429  //------------------------------------------------------------------------------------------------------------------------------
2430  bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
2431  {
2432  if (!m_wallet) return not_open(er);
2433  if (m_restricted)
2434  {
2436  er.message = "Command unavailable in restricted mode.";
2437  return false;
2438  }
2439 
2440  uint64_t min_height = 0, max_height = CRYPTONOTE_MAX_BLOCK_NUMBER;
2441  if (req.filter_by_height)
2442  {
2443  min_height = req.min_height;
2444  max_height = req.max_height <= max_height ? req.max_height : max_height;
2445  }
2446 
2447  boost::optional<uint32_t> account_index = req.account_index;
2448  std::set<uint32_t> subaddr_indices = req.subaddr_indices;
2449  if (req.all_accounts)
2450  {
2451  account_index = boost::none;
2452  subaddr_indices.clear();
2453  }
2454 
2455  if (req.in)
2456  {
2457  std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
2458  m_wallet->get_payments(payments, min_height, max_height, account_index, subaddr_indices);
2459  for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2460  res.in.push_back(wallet_rpc::transfer_entry());
2461  fill_transfer_entry(res.in.back(), i->second.m_tx_hash, i->first, i->second);
2462  }
2463  }
2464 
2465  if (req.out)
2466  {
2467  std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
2468  m_wallet->get_payments_out(payments, min_height, max_height, account_index, subaddr_indices);
2469  for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2470  res.out.push_back(wallet_rpc::transfer_entry());
2471  fill_transfer_entry(res.out.back(), i->first, i->second);
2472  }
2473  }
2474 
2475  if (req.migration)
2476  {
2477  std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
2478  m_wallet->get_payments_out_migration(payments, min_height, max_height, account_index, subaddr_indices);
2479  for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2480  res.migration.push_back(wallet_rpc::transfer_entry());
2481  fill_transfer_entry(res.migration.back(), i->first, i->second);
2482  }
2483  }
2484 
2485  if (req.pending || req.failed) {
2486  std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
2487  m_wallet->get_unconfirmed_payments_out(upayments, account_index, subaddr_indices);
2488  for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
2489  const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
2491  if (!((req.failed && is_failed) || (!is_failed && req.pending)))
2492  continue;
2493  std::list<wallet_rpc::transfer_entry> &entries = is_failed ? res.failed : res.pending;
2494  entries.push_back(wallet_rpc::transfer_entry());
2495  fill_transfer_entry(entries.back(), i->first, i->second);
2496  }
2497  }
2498 
2499  if (req.pool)
2500  {
2501  m_wallet->update_pool_state();
2502 
2503  std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
2504  m_wallet->get_unconfirmed_payments(payments, account_index, subaddr_indices);
2505  for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2506  res.pool.push_back(wallet_rpc::transfer_entry());
2507  fill_transfer_entry(res.pool.back(), i->first, i->second);
2508  }
2509  }
2510 
2511  return true;
2512  }
2513  //------------------------------------------------------------------------------------------------------------------------------
2514  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)
2515  {
2516  if (!m_wallet) return not_open(er);
2517  if (m_restricted)
2518  {
2520  er.message = "Command unavailable in restricted mode.";
2521  return false;
2522  }
2523 
2524  crypto::hash txid;
2525  cryptonote::blobdata txid_blob;
2526  if(!epee::string_tools::parse_hexstr_to_binbuff(req.txid, txid_blob))
2527  {
2529  er.message = "Transaction ID has invalid format";
2530  return false;
2531  }
2532 
2533  if(sizeof(txid) == txid_blob.size())
2534  {
2535  txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
2536  }
2537  else
2538  {
2540  er.message = "Transaction ID has invalid size: " + req.txid;
2541  return false;
2542  }
2543 
2544  if (req.account_index >= m_wallet->get_num_subaddress_accounts())
2545  {
2547  er.message = "Account index is out of bound";
2548  return false;
2549  }
2550 
2551  std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
2552  m_wallet->get_payments(payments, 0, (uint64_t)-1, req.account_index);
2553  for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2554  if (i->second.m_tx_hash == txid)
2555  {
2556  res.transfers.resize(res.transfers.size() + 1);
2557  fill_transfer_entry(res.transfers.back(), i->second.m_tx_hash, i->first, i->second);
2558  }
2559  }
2560 
2561  std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out;
2562  m_wallet->get_payments_out(payments_out, 0, (uint64_t)-1, req.account_index);
2563  for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
2564  if (i->first == txid)
2565  {
2566  res.transfers.resize(res.transfers.size() + 1);
2567  fill_transfer_entry(res.transfers.back(), i->first, i->second);
2568  }
2569  }
2570 
2571  std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> migrations;
2572  m_wallet->get_payments_out_migration(migrations, 0, (uint64_t)-1, req.account_index);
2573  for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = migrations.begin(); i != migrations.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::unconfirmed_transfer_details>> upayments;
2582  m_wallet->get_unconfirmed_payments_out(upayments, req.account_index);
2583  for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.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  m_wallet->update_pool_state();
2592 
2593  std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
2594  m_wallet->get_unconfirmed_payments(pool_payments, req.account_index);
2595  for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
2596  if (i->second.m_pd.m_tx_hash == txid)
2597  {
2598  res.transfers.resize(res.transfers.size() + 1);
2599  fill_transfer_entry(res.transfers.back(), i->first, i->second);
2600  }
2601  }
2602 
2603  if (!res.transfers.empty())
2604  {
2605  res.transfer = res.transfers.front(); // backward compat
2606  return true;
2607  }
2608 
2610  er.message = "Transaction not found.";
2611  return false;
2612  }
2613  //------------------------------------------------------------------------------------------------------------------------------
2614  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)
2615  {
2616  if (!m_wallet) return not_open(er);
2617  if (m_restricted)
2618  {
2620  er.message = "Command unavailable in restricted mode.";
2621  return false;
2622  }
2623  if (m_wallet->key_on_device())
2624  {
2626  er.message = "command not supported by HW wallet";
2627  return false;
2628  }
2629 
2630  try
2631  {
2632  res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str(req.all));
2633  }
2634  catch (const std::exception &e)
2635  {
2636  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2637  return false;
2638  }
2639 
2640  return true;
2641  }
2642  //------------------------------------------------------------------------------------------------------------------------------
2643  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)
2644  {
2645  if (!m_wallet) return not_open(er);
2646  if (m_restricted)
2647  {
2649  er.message = "Command unavailable in restricted mode.";
2650  return false;
2651  }
2652  if (m_wallet->key_on_device())
2653  {
2655  er.message = "command not supported by HW wallet";
2656  return false;
2657  }
2658 
2659  cryptonote::blobdata blob;
2660  if (!epee::string_tools::parse_hexstr_to_binbuff(req.outputs_data_hex, blob))
2661  {
2663  er.message = "Failed to parse hex.";
2664  return false;
2665  }
2666 
2667  try
2668  {
2669  res.num_imported = m_wallet->import_outputs_from_str(blob);
2670  }
2671  catch (const std::exception &e)
2672  {
2673  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2674  return false;
2675  }
2676 
2677  return true;
2678  }
2679  //------------------------------------------------------------------------------------------------------------------------------
2680  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)
2681  {
2682  if (!m_wallet) return not_open(er);
2683  try
2684  {
2685  std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->export_key_images(req.all);
2686  res.offset = ski.first;
2687  res.signed_key_images.resize(ski.second.size());
2688  for (size_t n = 0; n < ski.second.size(); ++n)
2689  {
2690  res.signed_key_images[n].key_image = epee::string_tools::pod_to_hex(ski.second[n].first);
2691  res.signed_key_images[n].signature = epee::string_tools::pod_to_hex(ski.second[n].second);
2692  }
2693  }
2694 
2695  catch (const std::exception& e)
2696  {
2697  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2698  return false;
2699  }
2700 
2701  return true;
2702  }
2703  //------------------------------------------------------------------------------------------------------------------------------
2704  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)
2705  {
2706  if (!m_wallet) return not_open(er);
2707  if (m_restricted)
2708  {
2710  er.message = "Command unavailable in restricted mode.";
2711  return false;
2712  }
2713  if (!m_wallet->is_trusted_daemon())
2714  {
2716  er.message = "This command requires a trusted daemon.";
2717  return false;
2718  }
2719  try
2720  {
2721  std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
2722  ski.resize(req.signed_key_images.size());
2723  for (size_t n = 0; n < ski.size(); ++n)
2724  {
2725  if (!epee::string_tools::hex_to_pod(req.signed_key_images[n].key_image, ski[n].first))
2726  {
2728  er.message = "failed to parse key image";
2729  return false;
2730  }
2731 
2732  if (!epee::string_tools::hex_to_pod(req.signed_key_images[n].signature, ski[n].second))
2733  {
2735  er.message = "failed to parse signature";
2736  return false;
2737  }
2738  }
2739  uint64_t spent = 0, unspent = 0;
2740  uint64_t height = m_wallet->import_key_images(ski, req.offset, spent, unspent);
2741  res.spent = spent;
2742  res.unspent = unspent;
2743  res.height = height;
2744  }
2745 
2746  catch (const std::exception& e)
2747  {
2748  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2749  return false;
2750  }
2751 
2752  return true;
2753  }
2754  //------------------------------------------------------------------------------------------------------------------------------
2755  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)
2756  {
2757  if (!m_wallet) return not_open(er);
2759  std::string uri = m_wallet->make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error);
2760  if (uri.empty())
2761  {
2763  er.message = std::string("Cannot make URI from supplied parameters: ") + error;
2764  return false;
2765  }
2766 
2767  res.uri = uri;
2768  return true;
2769  }
2770  //------------------------------------------------------------------------------------------------------------------------------
2771  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)
2772  {
2773  if (!m_wallet) return not_open(er);
2775  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))
2776  {
2778  er.message = "Error parsing URI: " + error;
2779  return false;
2780  }
2781  return true;
2782  }
2783  //------------------------------------------------------------------------------------------------------------------------------
2784  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)
2785  {
2786  if (!m_wallet) return not_open(er);
2787  const auto ab = m_wallet->get_address_book();
2788  if (req.entries.empty())
2789  {
2790  uint64_t idx = 0;
2791  for (const auto &entry: ab)
2792  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});
2793  }
2794  else
2795  {
2796  for (uint64_t idx: req.entries)
2797  {
2798  if (idx >= ab.size())
2799  {
2801  er.message = "Index out of range: " + std::to_string(idx);
2802  return false;
2803  }
2804  const auto &entry = ab[idx];
2805  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});
2806  }
2807  }
2808  return true;
2809  }
2810  //------------------------------------------------------------------------------------------------------------------------------
2811  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)
2812  {
2813  if (!m_wallet) return not_open(er);
2814  if (m_restricted)
2815  {
2817  er.message = "Command unavailable in restricted mode.";
2818  return false;
2819  }
2820 
2822  crypto::hash payment_id = crypto::null_hash;
2823  er.message = "";
2824  if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), req.address,
2825  [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
2826  if (!dnssec_valid)
2827  {
2828  er.message = std::string("Invalid DNSSEC for ") + url;
2829  return {};
2830  }
2831  if (addresses.empty())
2832  {
2833  er.message = std::string("No ETN address found at ") + url;
2834  return {};
2835  }
2836  return addresses[0];
2837  }))
2838  {
2840  if (er.message.empty())
2841  er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
2842  return false;
2843  }
2844  if (info.has_payment_id)
2845  {
2846  memcpy(payment_id.data, info.payment_id.data, 8);
2847  memset(payment_id.data + 8, 0, 24);
2848  }
2849  if (!req.payment_id.empty())
2850  {
2851  if (info.has_payment_id)
2852  {
2854  er.message = "Separate payment ID given with integrated address";
2855  return false;
2856  }
2857 
2858  crypto::hash long_payment_id;
2859 
2860  if (!wallet2::parse_long_payment_id(req.payment_id, payment_id))
2861  {
2862  if (!wallet2::parse_short_payment_id(req.payment_id, info.payment_id))
2863  {
2865  er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 64 character string";
2866  return false;
2867  }
2868  else
2869  {
2871  er.message = "Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address";
2872  return false;
2873  }
2874  }
2875  }
2876  if (!m_wallet->add_address_book_row(info.address, payment_id, req.description, info.is_subaddress))
2877  {
2879  er.message = "Failed to add address book entry";
2880  return false;
2881  }
2882  res.index = m_wallet->get_address_book().size() - 1;
2883  return true;
2884  }
2885  //------------------------------------------------------------------------------------------------------------------------------
2886  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)
2887  {
2888  if (!m_wallet) return not_open(er);
2889  if (m_restricted)
2890  {
2892  er.message = "Command unavailable in restricted mode.";
2893  return false;
2894  }
2895 
2896  const auto ab = m_wallet->get_address_book();
2897  if (req.index >= ab.size())
2898  {
2900  er.message = "Index out of range: " + std::to_string(req.index);
2901  return false;
2902  }
2903  if (!m_wallet->delete_address_book_row(req.index))
2904  {
2906  er.message = "Failed to delete address book entry";
2907  return false;
2908  }
2909  return true;
2910  }
2911  //------------------------------------------------------------------------------------------------------------------------------
2912  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)
2913  {
2914  if (!m_wallet) return not_open(er);
2915  if (m_restricted)
2916  {
2918  er.message = "Command unavailable in restricted mode.";
2919  return false;
2920  }
2921  try
2922  {
2923  m_wallet->refresh(m_wallet->is_trusted_daemon(), req.start_height, res.blocks_fetched, res.received_etn);
2924  return true;
2925  }
2926  catch (const std::exception& e)
2927  {
2928  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2929  return false;
2930  }
2931  return true;
2932  }
2933  //------------------------------------------------------------------------------------------------------------------------------
2934  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)
2935  {
2936  if (m_restricted)
2937  {
2939  er.message = "Command unavailable in restricted mode.";
2940  return false;
2941  }
2942  try
2943  {
2944  m_auto_refresh_period = req.enable ? req.period ? req.period : DEFAULT_AUTO_REFRESH_PERIOD : 0;
2945  MINFO("Auto refresh now " << (m_auto_refresh_period ? std::to_string(m_auto_refresh_period) + " seconds" : std::string("disabled")));
2946  return true;
2947  }
2948  catch (const std::exception& e)
2949  {
2950  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2951  return false;
2952  }
2953  return true;
2954  }
2955  //------------------------------------------------------------------------------------------------------------------------------
2956  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)
2957  {
2958  if (!m_wallet) return not_open(er);
2959  if (m_restricted)
2960  {
2962  er.message = "Command unavailable in restricted mode.";
2963  return false;
2964  }
2965  try
2966  {
2967  m_wallet->rescan_spent();
2968  return true;
2969  }
2970  catch (const std::exception& e)
2971  {
2972  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
2973  return false;
2974  }
2975  return true;
2976  }
2977  //------------------------------------------------------------------------------------------------------------------------------
2978  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)
2979  {
2980  if (!m_wallet) return not_open(er);
2981  if (!m_wallet->is_trusted_daemon())
2982  {
2984  er.message = "This command requires a trusted daemon.";
2985  return false;
2986  }
2987 
2988  size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2));
2989  if (req.threads_count < 1 || max_mining_threads_count < req.threads_count)
2990  {
2992  er.message = "The specified number of threads is inappropriate.";
2993  return false;
2994  }
2995 
2997  daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
2998  daemon_req.threads_count = req.threads_count;
2999  daemon_req.do_background_mining = req.do_background_mining;
3000  daemon_req.ignore_battery = req.ignore_battery;
3001 
3003  bool r = m_wallet->invoke_http_json("/start_mining", daemon_req, daemon_res);
3004  if (!r || daemon_res.status != CORE_RPC_STATUS_OK)
3005  {
3007  er.message = "Couldn't start mining due to unknown error.";
3008  return false;
3009  }
3010  return true;
3011  }
3012  //------------------------------------------------------------------------------------------------------------------------------
3013  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)
3014  {
3015  if (!m_wallet) return not_open(er);
3018  bool r = m_wallet->invoke_http_json("/stop_mining", daemon_req, daemon_res);
3019  if (!r || daemon_res.status != CORE_RPC_STATUS_OK)
3020  {
3022  er.message = "Couldn't stop mining due to unknown error.";
3023  return false;
3024  }
3025  return true;
3026  }
3027  //------------------------------------------------------------------------------------------------------------------------------
3028  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)
3029  {
3031  crypto::ElectrumWords::get_language_list(res.languages_local, false);
3032  return true;
3033  }
3034  //------------------------------------------------------------------------------------------------------------------------------
3035  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)
3036  {
3037  if (m_wallet_dir.empty())
3038  {
3040  er.message = "No wallet dir configured";
3041  return false;
3042  }
3043 
3044  namespace po = boost::program_options;
3045  po::variables_map vm2;
3046  const char *ptr = strchr(req.filename.c_str(), '/');
3047 #ifdef _WIN32
3048  if (!ptr)
3049  ptr = strchr(req.filename.c_str(), '\\');
3050  if (!ptr)
3051  ptr = strchr(req.filename.c_str(), ':');
3052 #endif
3053  if (ptr)
3054  {
3056  er.message = "Invalid filename";
3057  return false;
3058  }
3059  std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
3060  {
3061  std::vector<std::string> languages;
3062  crypto::ElectrumWords::get_language_list(languages, false);
3063  std::vector<std::string>::iterator it;
3064 
3065  it = std::find(languages.begin(), languages.end(), req.language);
3066  if (it == languages.end())
3067  {
3069  it = std::find(languages.begin(), languages.end(), req.language);
3070  }
3071  if (it == languages.end())
3072  {
3074  er.message = "Unknown language: " + req.language;
3075  return false;
3076  }
3077  }
3078  {
3079  po::options_description desc("dummy");
3080  const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
3081  const char *argv[4];
3082  int argc = 3;
3083  argv[0] = "wallet-rpc";
3084  argv[1] = "--password";
3085  argv[2] = req.password.c_str();
3086  argv[3] = NULL;
3087  vm2 = *m_vm;
3088  command_line::add_arg(desc, arg_password);
3089  po::store(po::parse_command_line(argc, argv, desc), vm2);
3090  }
3091  std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, true, nullptr).first;
3092  if (!wal)
3093  {
3095  er.message = "Failed to create wallet";
3096  return false;
3097  }
3098  wal->set_seed_language(req.language);
3101  hres.height = 0;
3102  bool r = wal->invoke_http_json("/getheight", hreq, hres);
3103  if (r)
3104  wal->set_refresh_from_block_height(hres.height);
3105  crypto::secret_key dummy_key;
3106  try {
3107  wal->generate(wallet_file, req.password, dummy_key, false, false);
3108  }
3109  catch (const std::exception& e)
3110  {
3111  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3112  return false;
3113  }
3114  if (!wal)
3115  {
3117  er.message = "Failed to generate wallet";
3118  return false;
3119  }
3120 
3121  if (m_wallet)
3122  {
3123  try
3124  {
3125  m_wallet->store();
3126  }
3127  catch (const std::exception& e)
3128  {
3129  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3130  return false;
3131  }
3132  delete m_wallet;
3133  }
3134  m_wallet = wal.release();
3135  return true;
3136  }
3137  //------------------------------------------------------------------------------------------------------------------------------
3138  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)
3139  {
3140  if (m_wallet_dir.empty())
3141  {
3143  er.message = "No wallet dir configured";
3144  return false;
3145  }
3146 
3147  namespace po = boost::program_options;
3148  po::variables_map vm2;
3149  const char *ptr = strchr(req.filename.c_str(), '/');
3150 #ifdef _WIN32
3151  if (!ptr)
3152  ptr = strchr(req.filename.c_str(), '\\');
3153  if (!ptr)
3154  ptr = strchr(req.filename.c_str(), ':');
3155 #endif
3156  if (ptr)
3157  {
3159  er.message = "Invalid filename";
3160  return false;
3161  }
3162  if (m_wallet && req.autosave_current)
3163  {
3164  try
3165  {
3166  m_wallet->store();
3167  }
3168  catch (const std::exception& e)
3169  {
3170  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3171  return false;
3172  }
3173  }
3174  std::string wallet_file = m_wallet_dir + "/" + req.filename;
3175  {
3176  po::options_description desc("dummy");
3177  const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
3178  const char *argv[4];
3179  int argc = 3;
3180  argv[0] = "wallet-rpc";
3181  argv[1] = "--password";
3182  argv[2] = req.password.c_str();
3183  argv[3] = NULL;
3184  vm2 = *m_vm;
3185  command_line::add_arg(desc, arg_password);
3186  po::store(po::parse_command_line(argc, argv, desc), vm2);
3187  }
3188  std::unique_ptr<tools::wallet2> wal = nullptr;
3189  try {
3190  wal = tools::wallet2::make_from_file(vm2, true, wallet_file, nullptr).first;
3191  }
3192  catch (const std::exception& e)
3193  {
3194  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3195  }
3196  if (!wal)
3197  {
3199  er.message = "Failed to open wallet";
3200  return false;
3201  }
3202 
3203  if (m_wallet)
3204  delete m_wallet;
3205  m_wallet = wal.release();
3206  return true;
3207  }
3208  //------------------------------------------------------------------------------------------------------------------------------
3209  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)
3210  {
3211  if (!m_wallet) return not_open(er);
3212 
3213  if (req.autosave_current)
3214  {
3215  try
3216  {
3217  m_wallet->store();
3218  }
3219  catch (const std::exception& e)
3220  {
3221  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3222  return false;
3223  }
3224  }
3225  delete m_wallet;
3226  m_wallet = NULL;
3227  return true;
3228  }
3229  //------------------------------------------------------------------------------------------------------------------------------
3230  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)
3231  {
3232  if (!m_wallet) return not_open(er);
3233  if (m_restricted)
3234  {
3236  er.message = "Command unavailable in restricted mode.";
3237  return false;
3238  }
3239  if (m_wallet->verify_password(req.old_password))
3240  {
3241  try
3242  {
3243  m_wallet->change_password(m_wallet->get_wallet_file(), req.old_password, req.new_password);
3244  LOG_PRINT_L0("Wallet password changed.");
3245  }
3246  catch (const std::exception& e)
3247  {
3248  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3249  return false;
3250  }
3251  }
3252  else
3253  {
3255  er.message = "Invalid original password.";
3256  return false;
3257  }
3258  return true;
3259  }
3260  //------------------------------------------------------------------------------------------------------------------------------
3261  void wallet_rpc_server::handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code) {
3262  try
3263  {
3264  std::rethrow_exception(e);
3265  }
3266  catch (const tools::error::no_connection_to_daemon& e)
3267  {
3269  er.message = e.what();
3270  }
3271  catch (const tools::error::daemon_busy& e)
3272  {
3274  er.message = e.what();
3275  }
3276  catch (const tools::error::zero_destination& e)
3277  {
3279  er.message = e.what();
3280  }
3281  catch (const tools::error::not_enough_etn& e)
3282  {
3284  er.message = e.what();
3285  }
3286  catch (const tools::error::not_enough_unlocked_etn& e)
3287  {
3289  er.message = e.what();
3290  }
3291  catch (const tools::error::tx_not_possible& e)
3292  {
3294  er.message = (boost::format(tr("Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee)")) %
3296  cryptonote::print_etn(e.tx_amount() + e.fee()) %
3298  cryptonote::print_etn(e.fee())).str();
3299  er.message = e.what();
3300  }
3301  catch (const tools::error::not_enough_outs_to_mix& e)
3302  {
3304  er.message = e.what() + std::string(" Please use sweep_dust.");
3305  }
3306  catch (const error::file_exists& e)
3307  {
3309  er.message = "Cannot create wallet. Already exists.";
3310  }
3311  catch (const error::invalid_password& e)
3312  {
3314  er.message = "Invalid password.";
3315  }
3316  catch (const error::account_index_outofbound& e)
3317  {
3319  er.message = e.what();
3320  }
3321  catch (const error::address_index_outofbound& e)
3322  {
3324  er.message = e.what();
3325  }
3326  catch (const error::signature_check_failed& e)
3327  {
3329  er.message = e.what();
3330  }
3331  catch (const std::exception& e)
3332  {
3333  er.code = default_error_code;
3334  er.message = e.what();
3335  }
3336  catch (...)
3337  {
3339  er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
3340  }
3341  }
3342  //------------------------------------------------------------------------------------------------------------------------------
3343  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)
3344  {
3345  if (m_wallet_dir.empty())
3346  {
3348  er.message = "No wallet dir configured";
3349  return false;
3350  }
3351 
3352  // early check for mandatory fields
3353  if (req.viewkey.empty())
3354  {
3356  er.message = "field 'viewkey' is mandatory. Please provide a view key you want to restore from.";
3357  return false;
3358  }
3359  if (req.address.empty())
3360  {
3362  er.message = "field 'address' is mandatory. Please provide a public address.";
3363  return false;
3364  }
3365 
3366  namespace po = boost::program_options;
3367  po::variables_map vm2;
3368  const char *ptr = strchr(req.filename.c_str(), '/');
3369  #ifdef _WIN32
3370  if (!ptr)
3371  ptr = strchr(req.filename.c_str(), '\\');
3372  if (!ptr)
3373  ptr = strchr(req.filename.c_str(), ':');
3374  #endif
3375  if (ptr)
3376  {
3378  er.message = "Invalid filename";
3379  return false;
3380  }
3381  std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
3382  // check if wallet file already exists
3383  if (!wallet_file.empty())
3384  {
3385  try
3386  {
3387  boost::system::error_code ignored_ec;
3388  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(wallet_file, ignored_ec), error::file_exists, wallet_file);
3389  }
3390  catch (const std::exception &e)
3391  {
3393  er.message = "Wallet already exists.";
3394  return false;
3395  }
3396  }
3397 
3398  {
3399  po::options_description desc("dummy");
3400  const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
3401  const char *argv[4];
3402  int argc = 3;
3403  argv[0] = "wallet-rpc";
3404  argv[1] = "--password";
3405  argv[2] = req.password.c_str();
3406  argv[3] = NULL;
3407  vm2 = *m_vm;
3408  command_line::add_arg(desc, arg_password);
3409  po::store(po::parse_command_line(argc, argv, desc), vm2);
3410  }
3411 
3412  auto rc = tools::wallet2::make_new(vm2, true, nullptr);
3413  std::unique_ptr<wallet2> wal;
3414  wal = std::move(rc.first);
3415  if (!wal)
3416  {
3418  er.message = "Failed to create wallet";
3419  return false;
3420  }
3421 
3423  if(!get_account_address_from_str(info, wal->nettype(), req.address))
3424  {
3426  er.message = "Failed to parse public address";
3427  return false;
3428  }
3429 
3430  epee::wipeable_string password = rc.second.password();
3431  epee::wipeable_string viewkey_string = req.viewkey;
3432  crypto::secret_key viewkey;
3433  if (!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
3434  {
3436  er.message = "Failed to parse view key secret key";
3437  return false;
3438  }
3439 
3440  if (m_wallet && req.autosave_current)
3441  {
3442  try
3443  {
3444  if (!wallet_file.empty())
3445  m_wallet->store();
3446  }
3447  catch (const std::exception &e)
3448  {
3449  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3450  return false;
3451  }
3452  }
3453 
3454  try
3455  {
3456  if (!req.spendkey.empty())
3457  {
3458  epee::wipeable_string spendkey_string = req.spendkey;
3460  if (!spendkey_string.hex_to_pod(unwrap(unwrap(spendkey))))
3461  {
3463  er.message = "Failed to parse spend key secret key";
3464  return false;
3465  }
3466  wal->generate(wallet_file, std::move(rc.second).password(), info.address, spendkey, viewkey, false);
3467  res.info = "Wallet has been generated successfully.";
3468  }
3469  else
3470  {
3471  wal->generate(wallet_file, std::move(rc.second).password(), info.address, viewkey, false);
3472  res.info = "Watch-only wallet has been generated successfully.";
3473  }
3474  MINFO("Wallet has been generated.\n");
3475  }
3476  catch (const std::exception &e)
3477  {
3478  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3479  return false;
3480  }
3481 
3482  if (!wal)
3483  {
3485  er.message = "Failed to generate wallet";
3486  return false;
3487  }
3488 
3489  // set blockheight if given
3490  try
3491  {
3492  wal->set_refresh_from_block_height(req.restore_height);
3493  wal->rewrite(wallet_file, password);
3494  }
3495  catch (const std::exception &e)
3496  {
3497  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3498  return false;
3499  }
3500 
3501  if (m_wallet)
3502  delete m_wallet;
3503  m_wallet = wal.release();
3504  res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3505  return true;
3506  }
3507  //------------------------------------------------------------------------------------------------------------------------------
3508  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)
3509  {
3510  if (m_wallet_dir.empty())
3511  {
3513  er.message = "No wallet dir configured";
3514  return false;
3515  }
3516 
3517  // early check for mandatory fields
3518  if (req.seed.empty())
3519  {
3521  er.message = "field 'seed' is mandatory. Please provide a seed you want to restore from.";
3522  return false;
3523  }
3524 
3525  namespace po = boost::program_options;
3526  po::variables_map vm2;
3527  const char *ptr = strchr(req.filename.c_str(), '/');
3528  #ifdef _WIN32
3529  if (!ptr)
3530  ptr = strchr(req.filename.c_str(), '\\');
3531  if (!ptr)
3532  ptr = strchr(req.filename.c_str(), ':');
3533  #endif
3534  if (ptr)
3535  {
3537  er.message = "Invalid filename";
3538  return false;
3539  }
3540  std::string wallet_file = req.filename.empty() ? "" : (m_wallet_dir + "/" + req.filename);
3541  // check if wallet file already exists
3542  if (!wallet_file.empty())
3543  {
3544  try
3545  {
3546  boost::system::error_code ignored_ec;
3547  THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(wallet_file, ignored_ec), error::file_exists, wallet_file);
3548  }
3549  catch (const std::exception &e)
3550  {
3552  er.message = "Wallet already exists.";
3553  return false;
3554  }
3555  }
3556  crypto::secret_key recovery_key;
3557  std::string old_language;
3558 
3559  // check the given seed
3560  {
3561  if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, old_language))
3562  {
3564  er.message = "Electrum-style word list failed verification";
3565  return false;
3566  }
3567  }
3568  if (m_wallet && req.autosave_current)
3569  {
3570  try
3571  {
3572  m_wallet->store();
3573  }
3574  catch (const std::exception &e)
3575  {
3576  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3577  return false;
3578  }
3579  }
3580 
3581  // process seed_offset if given
3582  {
3583  if (!req.seed_offset.empty())
3584  {
3585  recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset);
3586  }
3587  }
3588  {
3589  po::options_description desc("dummy");
3590  const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
3591  const char *argv[4];
3592  int argc = 3;
3593  argv[0] = "wallet-rpc";
3594  argv[1] = "--password";
3595  argv[2] = req.password.c_str();
3596  argv[3] = NULL;
3597  vm2 = *m_vm;
3598  command_line::add_arg(desc, arg_password);
3599  po::store(po::parse_command_line(argc, argv, desc), vm2);
3600  }
3601 
3602  auto rc = tools::wallet2::make_new(vm2, true, nullptr);
3603  std::unique_ptr<wallet2> wal;
3604  wal = std::move(rc.first);
3605  if (!wal)
3606  {
3608  er.message = "Failed to create wallet";
3609  return false;
3610  }
3611 
3612  epee::wipeable_string password = rc.second.password();
3613 
3614  bool was_deprecated_wallet = ((old_language == crypto::ElectrumWords::old_language_name) ||
3616 
3617  std::string mnemonic_language = old_language;
3618  if (was_deprecated_wallet)
3619  {
3620  // The user had used an older version of the wallet with old style mnemonics.
3621  res.was_deprecated = true;
3622  }
3623 
3624  if (old_language == crypto::ElectrumWords::old_language_name)
3625  {
3626  if (req.language.empty())
3627  {
3629  er.message = "Wallet was using the old seed language. You need to specify a new seed language.";
3630  return false;
3631  }
3632  std::vector<std::string> language_list;
3633  std::vector<std::string> language_list_en;
3635  crypto::ElectrumWords::get_language_list(language_list_en, true);
3636  if (std::find(language_list.begin(), language_list.end(), req.language) == language_list.end() &&
3637  std::find(language_list_en.begin(), language_list_en.end(), req.language) == language_list_en.end())
3638  {
3640  er.message = "Wallet was using the old seed language, and the specified new seed language is invalid.";
3641  return false;
3642  }
3643  mnemonic_language = req.language;
3644  }
3645 
3646  wal->set_seed_language(mnemonic_language);
3647 
3648  crypto::secret_key recovery_val;
3649  try
3650  {
3651  recovery_val = wal->generate(wallet_file, std::move(rc.second).password(), recovery_key, true, false, false);
3652  MINFO("Wallet has been restored.\n");
3653  }
3654  catch (const std::exception &e)
3655  {
3656  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3657  return false;
3658  }
3659 
3660  // // Convert the secret key back to seed
3661  epee::wipeable_string electrum_words;
3662  if (!crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language))
3663  {
3665  er.message = "Failed to encode seed";
3666  return false;
3667  }
3668  res.seed = std::string(electrum_words.data(), electrum_words.size());
3669 
3670  if (!wal)
3671  {
3673  er.message = "Failed to generate wallet";
3674  return false;
3675  }
3676 
3677  // set blockheight if given
3678  try
3679  {
3680  wal->set_refresh_from_block_height(req.restore_height);
3681  wal->rewrite(wallet_file, password);
3682  }
3683  catch (const std::exception &e)
3684  {
3685  handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
3686  return false;
3687  }
3688 
3689  if (m_wallet)
3690  delete m_wallet;
3691  m_wallet = wal.release();
3692  res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3693  res.info = "Wallet has been restored successfully.";
3694  return true;
3695  }
3696  //------------------------------------------------------------------------------------------------------------------------------
3697  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)
3698  {
3699  if (!m_wallet) return not_open(er);
3700  res.multisig = m_wallet->multisig(&res.ready, &res.threshold, &res.total);
3701  return true;
3702  }
3703  //------------------------------------------------------------------------------------------------------------------------------
3704  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)
3705  {
3706  if (!m_wallet) return not_open(er);
3707  if (m_restricted)
3708  {
3710  er.message = "Command unavailable in restricted mode.";
3711  return false;
3712  }
3713  if (m_wallet->multisig())
3714  {
3716  er.message = "This wallet is already multisig";
3717  return false;
3718  }
3719  if (m_wallet->watch_only())
3720  {
3722  er.message = "wallet is watch-only and cannot be made multisig";
3723  return false;
3724  }
3725 
3726  res.multisig_info = m_wallet->get_multisig_info();
3727  return true;
3728  }
3729  //------------------------------------------------------------------------------------------------------------------------------
3730  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)
3731  {
3732  if (!m_wallet) return not_open(er);
3733  if (m_restricted)
3734  {
3736  er.message = "Command unavailable in restricted mode.";
3737  return false;
3738  }
3739  if (m_wallet->multisig())
3740  {
3742  er.message = "This wallet is already multisig";
3743  return false;
3744  }
3745  if (m_wallet->watch_only())
3746  {
3748  er.message = "wallet is watch-only and cannot be made multisig";
3749  return false;
3750  }
3751 
3752  try
3753  {
3754  res.multisig_info = m_wallet->make_multisig(req.password, req.multisig_info, req.threshold);
3755  res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3756  }
3757  catch (const std::exception &e)
3758  {
3760  er.message = e.what();
3761  return false;
3762  }
3763 
3764  return true;
3765  }
3766  //------------------------------------------------------------------------------------------------------------------------------
3767  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)
3768  {
3769  if (!m_wallet) return not_open(er);
3770  if (m_restricted)
3771  {
3773  er.message = "Command unavailable in restricted mode.";
3774  return false;
3775  }
3776  bool ready;
3777  if (!m_wallet->multisig(&ready))
3778  {
3780  er.message = "This wallet is not multisig";
3781  return false;
3782  }
3783  if (!ready)
3784  {
3786  er.message = "This wallet is multisig, but not yet finalized";
3787  return false;
3788  }
3789 
3791  try
3792  {
3793  info = m_wallet->export_multisig();
3794  }
3795  catch (const std::exception &e)
3796  {
3798  er.message = e.what();
3799  return false;
3800  }
3801 
3803 
3804  return true;
3805  }
3806  //------------------------------------------------------------------------------------------------------------------------------
3807  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)
3808  {
3809  if (!m_wallet) return not_open(er);
3810  if (m_restricted)
3811  {
3813  er.message = "Command unavailable in restricted mode.";
3814  return false;
3815  }
3816  bool ready;
3817  uint32_t threshold, total;
3818  if (!m_wallet->multisig(&ready, &threshold, &total))
3819  {
3821  er.message = "This wallet is not multisig";
3822  return false;
3823  }
3824  if (!ready)
3825  {
3827  er.message = "This wallet is multisig, but not yet finalized";
3828  return false;
3829  }
3830 
3831  if (req.info.size() < threshold - 1)
3832  {
3834  er.message = "Needs multisig export info from more participants";
3835  return false;
3836  }
3837 
3838  std::vector<cryptonote::blobdata> info;
3839  info.resize(req.info.size());
3840  for (size_t n = 0; n < info.size(); ++n)
3841  {
3842  if (!epee::string_tools::parse_hexstr_to_binbuff(req.info[n], info[n]))
3843  {
3845  er.message = "Failed to parse hex.";
3846  return false;
3847  }
3848  }
3849 
3850  try
3851  {
3852  res.n_outputs = m_wallet->import_multisig(info);
3853  }
3854  catch (const std::exception &e)
3855  {
3857  er.message = "Error calling import_multisig";
3858  return false;
3859  }
3860 
3861  if (m_wallet->is_trusted_daemon())
3862  {
3863  try
3864  {
3865  m_wallet->rescan_spent();
3866  }
3867  catch (const std::exception &e)
3868  {
3869  er.message = std::string("Success, but failed to update spent status after import multisig info: ") + e.what();
3870  }
3871  }
3872  else
3873  {
3874  er.message = "Success, but cannot update spent status after import multisig info as daemon is untrusted";
3875  }
3876 
3877  return true;
3878  }
3879  //------------------------------------------------------------------------------------------------------------------------------
3880  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)
3881  {
3882  if (!m_wallet) return not_open(er);
3883  if (m_restricted)
3884  {
3886  er.message = "Command unavailable in restricted mode.";
3887  return false;
3888  }
3889  bool ready;
3890  uint32_t threshold, total;
3891  if (!m_wallet->multisig(&ready, &threshold, &total))
3892  {
3894  er.message = "This wallet is not multisig";
3895  return false;
3896  }
3897  if (ready)
3898  {
3900  er.message = "This wallet is multisig, and already finalized";
3901  return false;
3902  }
3903 
3904  if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
3905  {
3907  er.message = "Needs multisig info from more participants";
3908  return false;
3909  }
3910 
3911  try
3912  {
3913  if (!m_wallet->finalize_multisig(req.password, req.multisig_info))
3914  {
3916  er.message = "Error calling finalize_multisig";
3917  return false;
3918  }
3919  }
3920  catch (const std::exception &e)
3921  {
3923  er.message = std::string("Error calling finalize_multisig: ") + e.what();
3924  return false;
3925  }
3926  res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3927 
3928  return true;
3929  }
3930  //------------------------------------------------------------------------------------------------------------------------------
3931  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)
3932  {
3933  if (!m_wallet) return not_open(er);
3934  if (m_restricted)
3935  {
3937  er.message = "Command unavailable in restricted mode.";
3938  return false;
3939  }
3940  bool ready;
3941  uint32_t threshold, total;
3942  if (!m_wallet->multisig(&ready, &threshold, &total))
3943  {
3945  er.message = "This wallet is not multisig";
3946  return false;
3947  }
3948 
3949  if (ready)
3950  {
3952  er.message = "This wallet is multisig, and already finalized";
3953  return false;
3954  }
3955 
3956  if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
3957  {
3959  er.message = "Needs multisig info from more participants";
3960  return false;
3961  }
3962 
3963  try
3964  {
3965  res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info);
3966  if (res.multisig_info.empty())
3967  {
3968  res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3969  }
3970  }
3971  catch (const std::exception &e)
3972  {
3974  er.message = std::string("Error calling exchange_multisig_info: ") + e.what();
3975  return false;
3976  }
3977  return true;
3978  }
3979  //------------------------------------------------------------------------------------------------------------------------------
3980  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)
3981  {
3982  if (!m_wallet) return not_open(er);
3983  if (m_restricted)
3984  {
3986  er.message = "Command unavailable in restricted mode.";
3987  return false;
3988  }
3989  bool ready;
3990  uint32_t threshold, total;
3991  if (!m_wallet->multisig(&ready, &threshold, &total))
3992  {
3994  er.message = "This wallet is not multisig";
3995  return false;
3996  }
3997  if (!ready)
3998  {
4000  er.message = "This wallet is multisig, but not yet finalized";
4001  return false;
4002  }
4003 
4004  cryptonote::blobdata blob;
4005  if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob))
4006  {
4008  er.message = "Failed to parse hex.";
4009  return false;
4010  }
4011 
4013  bool r = m_wallet->load_multisig_tx(blob, txs, NULL);
4014  if (!r)
4015  {
4017  er.message = "Failed to parse multisig tx data.";
4018  return false;
4019  }
4020 
4021  std::vector<crypto::hash> txids;
4022  try
4023  {
4024  bool r = m_wallet->sign_multisig_tx(txs, txids);
4025  if (!r)
4026  {
4028  er.message = "Failed to sign multisig tx";
4029  return false;
4030  }
4031  }
4032  catch (const std::exception &e)
4033  {
4035  er.message = std::string("Failed to sign multisig tx: ") + e.what();
4036  return false;
4037  }
4038 
4039  res.tx_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(txs));
4040  if (!txids.empty())
4041  {
4042  for (const crypto::hash &txid: txids)
4043  res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(txid));
4044  }
4045 
4046  return true;
4047  }
4048  //------------------------------------------------------------------------------------------------------------------------------
4049  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)
4050  {
4051  if (!m_wallet) return not_open(er);
4052  if (m_restricted)
4053  {
4055  er.message = "Command unavailable in restricted mode.";
4056  return false;
4057  }
4058  bool ready;
4059  uint32_t threshold, total;
4060  if (!m_wallet->multisig(&ready, &threshold, &total))
4061  {
4063  er.message = "This wallet is not multisig";
4064  return false;
4065  }
4066  if (!ready)
4067  {
4069  er.message = "This wallet is multisig, but not yet finalized";
4070  return false;
4071  }
4072 
4073  cryptonote::blobdata blob;
4074  if (!epee::string_tools::parse_hexstr_to_binbuff(req.tx_data_hex, blob))
4075  {
4077  er.message = "Failed to parse hex.";
4078  return false;
4079  }
4080 
4082  bool r = m_wallet->load_multisig_tx(blob, txs, NULL);
4083  if (!r)
4084  {
4086  er.message = "Failed to parse multisig tx data.";
4087  return false;
4088  }
4089 
4090  if (txs.m_signers.size() < threshold)
4091  {
4093  er.message = "Not enough signers signed this transaction.";
4094  return false;
4095  }
4096 
4097  try
4098  {
4099  for (auto &ptx: txs.m_ptx)
4100  {
4101  m_wallet->commit_tx(ptx);
4103  }
4104  }
4105  catch (const std::exception &e)
4106  {
4108  er.message = std::string("Failed to submit multisig tx: ") + e.what();
4109  return false;
4110  }
4111 
4112  return true;
4113  }
4114  //------------------------------------------------------------------------------------------------------------------------------
4115  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)
4116  {
4118  static const struct { cryptonote::network_type type; const char *stype; } net_types[] = {
4119  { cryptonote::MAINNET, "mainnet" },
4120  { cryptonote::TESTNET, "testnet" },
4121  { cryptonote::STAGENET, "stagenet" },
4122  };
4123  if (!req.any_net_type && !m_wallet) return not_open(er);
4124  for (const auto &net_type: net_types)
4125  {
4126  if (!req.any_net_type && (!m_wallet || net_type.type != m_wallet->nettype()))
4127  continue;
4128  if (req.allow_openalias)
4129  {
4131  res.valid = get_account_address_from_str_or_url(info, net_type.type, req.address,
4132  [&er, &address](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
4133  if (!dnssec_valid)
4134  {
4135  er.message = std::string("Invalid DNSSEC for ") + url;
4136  return {};
4137  }
4138  if (addresses.empty())
4139  {
4140  er.message = std::string("No ETN address found at ") + url;
4141  return {};
4142  }
4143  address = addresses[0];
4144  return address;
4145  });
4146  if (res.valid)
4147  res.openalias_address = address;
4148  }
4149  else
4150  {
4151  res.valid = cryptonote::get_account_address_from_str(info, net_type.type, req.address);
4152  }
4153  if (res.valid)
4154  {
4155  res.integrated = info.has_payment_id;
4156  res.subaddress = info.is_subaddress;
4157  res.nettype = net_type.stype;
4158  return true;
4159  }
4160  }
4161 
4163  er.message = std::string("Invalid address");
4164  return false;
4165  }
4166  //------------------------------------------------------------------------------------------------------------------------------
4167  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)
4168  {
4169  if (!m_wallet) return not_open(er);
4170  if (m_restricted)
4171  {
4173  er.message = "Command unavailable in restricted mode.";
4174  return false;
4175  }
4176 
4177  std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints;
4178  ssl_allowed_fingerprints.reserve(req.ssl_allowed_fingerprints.size());
4179  for (const std::string &fp: req.ssl_allowed_fingerprints)
4180  {
4181  ssl_allowed_fingerprints.push_back({});
4182  std::vector<uint8_t> &v = ssl_allowed_fingerprints.back();
4183  for (auto c: fp)
4184  v.push_back(c);
4185  }
4186 
4188  if (req.ssl_allow_any_cert)
4190  else if (!ssl_allowed_fingerprints.empty() || !req.ssl_ca_file.empty())
4191  ssl_options = epee::net_utils::ssl_options_t{std::move(ssl_allowed_fingerprints), std::move(req.ssl_ca_file)};
4192 
4193  if (!epee::net_utils::ssl_support_from_string(ssl_options.support, req.ssl_support))
4194  {
4196  er.message = std::string("Invalid ssl support mode");
4197  return false;
4198  }
4199 
4201  std::move(req.ssl_private_key_path), std::move(req.ssl_certificate_path)
4202  };
4203 
4204  const bool verification_required =
4207 
4208  if (verification_required && !ssl_options.has_strong_verification(boost::string_ref{}))
4209  {
4211  er.message = "SSL is enabled but no user certificate or fingerprints were provided";
4212  return false;
4213  }
4214 
4215  if (!m_wallet->set_daemon(req.address, boost::none, req.trusted, std::move(ssl_options)))
4216  {
4218  er.message = std::string("Unable to set daemon");
4219  return false;
4220  }
4221  return true;
4222  }
4223  //------------------------------------------------------------------------------------------------------------------------------
4224  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)
4225  {
4226  if (m_restricted)
4227  {
4229  er.message = "Command unavailable in restricted mode.";
4230  return false;
4231  }
4232 
4233  if (req.level < 0 || req.level > 4)
4234  {
4236  er.message = "Error: log level not valid";
4237  return false;
4238  }
4239  mlog_set_log_level(req.level);
4240  return true;
4241  }
4242  //------------------------------------------------------------------------------------------------------------------------------
4243  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)
4244  {
4245  if (m_restricted)
4246  {
4248  er.message = "Command unavailable in restricted mode.";
4249  return false;
4250  }
4251 
4252  mlog_set_log(req.categories.c_str());
4253  res.categories = mlog_get_categories();
4254  return true;
4255  }
4256  //------------------------------------------------------------------------------------------------------------------------------
4257  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)
4258  {
4259  res.version = WALLET_RPC_VERSION;
4260  return true;
4261  }
4262  //------------------------------------------------------------------------------------------------------------------------------
4263 }
4264 
4266 {
4267 private:
4268  const boost::program_options::variables_map& vm;
4269 
4270  std::unique_ptr<tools::wallet_rpc_server> wrpc;
4271 
4272 public:
4273  t_daemon(boost::program_options::variables_map const & _vm)
4274  : vm(_vm)
4275  , wrpc(new tools::wallet_rpc_server)
4276  {
4277  }
4278 
4279  bool run()
4280  {
4281  std::unique_ptr<tools::wallet2> wal;
4282  try
4283  {
4284  const bool testnet = tools::wallet2::has_testnet_option(vm);
4285  const bool stagenet = tools::wallet2::has_stagenet_option(vm);
4286  if (testnet && stagenet)
4287  {
4288  MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet"));
4289  return false;
4290  }
4291 
4293  const auto arg_from_json = wallet_args::arg_generate_from_json();
4294 
4295  const auto wallet_file = command_line::get_arg(vm, arg_wallet_file);
4296  const auto from_json = command_line::get_arg(vm, arg_from_json);
4297  const auto wallet_dir = command_line::get_arg(vm, arg_wallet_dir);
4298  const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password);
4299  const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
4300 
4301  if(!wallet_file.empty() && !from_json.empty())
4302  {
4303  LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json"));
4304  return false;
4305  }
4306 
4307  if (!wallet_dir.empty())
4308  {
4309  wal = NULL;
4310  goto just_dir;
4311  }
4312 
4313  if (wallet_file.empty() && from_json.empty())
4314  {
4315  LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir"));
4316  return false;
4317  }
4318 
4319  LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
4320  if(!wallet_file.empty())
4321  {
4322  wal = tools::wallet2::make_from_file(vm, true, wallet_file, password_prompt).first;
4323  }
4324  else
4325  {
4326  try
4327  {
4328  auto rc = tools::wallet2::make_from_json(vm, true, from_json, password_prompt);
4329  wal = std::move(rc.first);
4330  }
4331  catch (const std::exception &e)
4332  {
4333  MERROR("Error creating wallet: " << e.what());
4334  return false;
4335  }
4336  }
4337  if (!wal)
4338  {
4339  return false;
4340  }
4341 
4342  bool quit = false;
4343  tools::signal_handler::install([&wal, &quit](int) {
4344  assert(wal);
4345  quit = true;
4346  wal->stop();
4347  });
4348 
4349  wal->refresh(wal->is_trusted_daemon());
4350  // if we ^C during potentially length load/refresh, there's no server loop yet
4351  if (quit)
4352  {
4353  MINFO(tools::wallet_rpc_server::tr("Saving wallet..."));
4354  wal->store();
4355  MINFO(tools::wallet_rpc_server::tr("Successfully saved"));
4356  return false;
4357  }
4358  MINFO(tools::wallet_rpc_server::tr("Successfully loaded"));
4359  }
4360  catch (const std::exception& e)
4361  {
4362  LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what());
4363  return false;
4364  }
4365  just_dir:
4366  if (wal) wrpc->set_wallet(wal.release());
4367  bool r = wrpc->init(&vm);
4368  CHECK_AND_ASSERT_MES(r, false, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server"));
4369  tools::signal_handler::install([this](int) {
4370  wrpc->send_stop_signal();
4371  });
4372  LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server"));
4373  try
4374  {
4375  wrpc->run();
4376  }
4377  catch (const std::exception &e)
4378  {
4379  LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what());
4380  return false;
4381  }
4382  LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server"));
4383  try
4384  {
4385  LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet..."));
4386  wrpc->stop();
4387  LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved"));
4388  }
4389  catch (const std::exception& e)
4390  {
4391  LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what());
4392  return false;
4393  }
4394  return true;
4395  }
4396 
4397  void stop()
4398  {
4399  wrpc->send_stop_signal();
4400  }
4401 };
4402 
4403 class t_executor final
4404 {
4405 public:
4406  static std::string const NAME;
4407 
4408  typedef ::t_daemon t_daemon;
4409 
4410  std::string const & name() const
4411  {
4412  return NAME;
4413  }
4414 
4415  t_daemon create_daemon(boost::program_options::variables_map const & vm)
4416  {
4417  return t_daemon(vm);
4418  }
4419 
4420  bool run_non_interactive(boost::program_options::variables_map const & vm)
4421  {
4422  return t_daemon(vm).run();
4423  }
4424 
4425  bool run_interactive(boost::program_options::variables_map const & vm)
4426  {
4427  return t_daemon(vm).run();
4428  }
4429 };
4430 
4431 std::string const t_executor::NAME = "Wallet RPC Daemon";
4432 
4433 int main(int argc, char** argv) {
4434  TRY_ENTRY();
4435 
4436  namespace po = boost::program_options;
4437 
4439  const auto arg_from_json = wallet_args::arg_generate_from_json();
4440 
4441  po::options_description hidden_options("Hidden");
4442 
4443  po::options_description desc_params(wallet_args::tr("Wallet options"));
4444  tools::wallet2::init_options(desc_params);
4445  command_line::add_arg(desc_params, arg_rpc_bind_port);
4446  command_line::add_arg(desc_params, arg_disable_rpc_login);
4447  command_line::add_arg(desc_params, arg_restricted);
4449  command_line::add_arg(desc_params, arg_wallet_file);
4450  command_line::add_arg(desc_params, arg_from_json);
4451  command_line::add_arg(desc_params, arg_wallet_dir);
4452  command_line::add_arg(desc_params, arg_prompt_for_password);
4453 
4454  daemonizer::init_options(hidden_options, desc_params);
4455  desc_params.add(hidden_options);
4456 
4457  boost::optional<po::variables_map> vm;
4458  bool should_terminate = false;
4459  std::tie(vm, should_terminate) = wallet_args::main(
4460  argc, argv,
4461  "electroneum-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
4462  tools::wallet_rpc_server::tr("This is the RPC electroneum wallet. It needs to connect to a electroneum\ndaemon to work correctly."),
4463  desc_params,
4464  po::positional_options_description(),
4465  [](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(); },
4466  "electroneum-wallet-rpc.log",
4467  true
4468  );
4469  if (!vm)
4470  {
4471  return 1;
4472  }
4473  if (should_terminate)
4474  {
4475  return 0;
4476  }
4477 
4478  return daemonizer::daemonize(argc, const_cast<const char**>(argv), t_executor{}, *vm) ? 0 : 1;
4479  CATCH_ENTRY_L0("main", 1);
4480 }
return true
else if(0==res)
uint64_t height
Definition: blockchain.cpp:91
uint8_t threshold
Definition: blockchain.cpp:92
std::string get_public_address_str(network_type nettype) const
Definition: account.cpp:269
bool run(size_t threads_count, bool wait=true)
bool init(std::function< void(size_t, uint8_t *)> rng, const std::string &bind_port="0", const std::string &bind_ip="0.0.0.0", std::vector< std::string > access_control_origins=std::vector< std::string >(), boost::optional< net_utils::http::login > user=boost::none, net_utils::ssl_options_t ssl_options=net_utils::ssl_support_t::e_ssl_support_autodetect)
net_utils::boosted_tcp_server< net_utils::http::http_custom_handler< epee::net_utils::connection_context_base > > m_net_server
ssl_verification_t verification
Definition: net_ssl.h:82
bool has_strong_verification(boost::string_ref host) const noexcept
\retrurn True if host can be verified using this configuration WITHOUT system "root" CAs.
Definition: net_ssl.cpp:402
ssl_authentication_t auth
Definition: net_ssl.h:80
bool hex_to_pod(T &pod) const
const char * data() const noexcept
size_t size() const noexcept
t_daemon(boost::program_options::variables_map const &_vm)
static std::string const NAME
bool run_interactive(boost::program_options::variables_map const &vm)
bool run_non_interactive(boost::program_options::variables_map const &vm)
::t_daemon t_daemon
std::string const & name() const
t_daemon create_daemon(boost::program_options::variables_map const &vm)
static boost::optional< password_container > prompt(bool verify, const char *mesage="Password", bool hide_input=true)
Definition: password.cpp:253
std::FILE * handle() const noexcept
Definition: util.h:91
static private_file create(std::string filename)
Definition: util.cpp:125
static bool install(T t)
installs a signal handler
Definition: util.h:164
static bool has_testnet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1172
uint64_t balance_all(bool public_blockchain) const
Definition: wallet2.cpp:6182
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:1311
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:1249
void refresh(bool trusted_daemon)
Definition: wallet2.cpp:3043
void add_subaddress(uint32_t index_major, const std::string &label)
Definition: wallet2.cpp:1469
BackgroundMiningSetupType setup_background_mining() const
Definition: wallet2.h:1099
size_t get_num_subaddress_accounts() const
Definition: wallet2.h:795
std::map< uint32_t, uint64_t > balance_per_subaddress(uint32_t subaddr_index_major, bool public_blockchain=false) const
Definition: wallet2.cpp:6078
void add_subaddress_account(const std::string &label, const bool update_account_tags=true)
Definition: wallet2.cpp:1462
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:1226
std::string get_subaddress_label(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1517
void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label)
Definition: wallet2.cpp:1527
bool has_multisig_partial_key_images() const
Definition: wallet2.cpp:5537
void get_transfers(wallet2::transfer_container &incoming_transfers) const
Definition: wallet2.cpp:6205
std::vector< transfer_details > transfer_container
Definition: wallet2.h:443
uint64_t get_last_block_reward() const
Definition: wallet2.h:898
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:11826
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:1232
boost::optional< cryptonote::subaddress_index > get_subaddress_index(const cryptonote::account_public_address &address) const
Definition: wallet2.cpp:1436
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:11846
size_t get_num_subaddresses(uint32_t index_major) const
Definition: wallet2.h:796
uint64_t get_blockchain_current_height() const
Definition: wallet2.h:890
void set_account_tag_description(const std::string &tag, const std::string &description)
Set the label of the given tag.
Definition: wallet2.cpp:11859
bool multisig(bool *ready=NULL, uint32_t *threshold=NULL, uint32_t *total=NULL) const
Definition: wallet2.cpp:5524
uint64_t unlocked_balance(uint32_t subaddr_index_major, bool public_blockchain, uint64_t *blocks_to_unlock=NULL) const
Definition: wallet2.cpp:6062
static bool parse_short_payment_id(const std::string &payment_id_str, crypto::hash8 &payment_id)
Definition: wallet2.cpp:5615
BackgroundMiningSetupType
Definition: wallet2.h:231
@ BackgroundMiningNo
Definition: wallet2.h:234
@ BackgroundMiningMaybe
Definition: wallet2.h:232
static bool has_stagenet_option(const boost::program_options::variables_map &vm)
Definition: wallet2.cpp:1177
bool is_trusted_daemon() const
Definition: wallet2.h:759
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:6139
std::string get_subaddress_as_str(const cryptonote::subaddress_index &index) const
Definition: wallet2.cpp:1451
uint64_t unlocked_balance_all(bool public_blockchain, uint64_t *blocks_to_unlock=NULL) const
Definition: wallet2.cpp:6190
bool synced_to_v10() const
Definition: wallet2.h:891
cryptonote::account_base & get_account()
Definition: wallet2.h:728
uint64_t balance(uint32_t subaddr_index_major, bool public_blockchain) const
Definition: wallet2.cpp:6052
static void init_options(boost::program_options::options_description &desc_params)
Definition: wallet2.cpp:1192
void account_major_offset(uint32_t offset)
Definition: wallet2.h:804
std::string get_tx_note(const crypto::hash &txid) const
Definition: wallet2.cpp:11782
cryptonote::network_type nettype() const
Definition: wallet2.h:818
bool init(const boost::program_options::variables_map *vm)
static const char * tr(const char *str)
#define tr(x)
Definition: common_defines.h:4
#define CORE_RPC_STATUS_OK
#define CRYPTONOTE_MAX_BLOCK_NUMBER
Mnemonic seed generation and wallet restoration from them.
void * memcpy(void *a, const void *b, size_t c)
const uint32_t T[512]
const char * res
Definition: hmac_keccak.cpp:41
const char * key
Definition: hmac_keccak.cpp:39
const char * i18n_translate(const char *s, const std::string &context)
Definition: i18n.cpp:322
#define AUTO_VAL_INIT(v)
Definition: misc_language.h:53
#define MERROR(x)
Definition: misc_log_ex.h:73
#define LOG_PRINT_L3(x)
Definition: misc_log_ex.h:102
#define MDEBUG(x)
Definition: misc_log_ex.h:76
std::string mlog_get_categories()
Definition: mlog.cpp:276
void mlog_set_log(const char *log)
Definition: mlog.cpp:288
void mlog_set_log_level(int level)
Definition: mlog.cpp:282
#define CATCH_ENTRY_L0(lacation, return_val)
Definition: misc_log_ex.h:165
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
Definition: misc_log_ex.h:181
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
#define MINFO(x)
Definition: misc_log_ex.h:75
#define LOG_PRINT_L2(x)
Definition: misc_log_ex.h:101
#define TRY_ENTRY()
Definition: misc_log_ex.h:151
#define LOG_PRINT_L0(x)
Definition: misc_log_ex.h:99
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
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
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
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
std::vector< const Language::Base * > get_language_list()
bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string &dst, size_t len, bool duplicate, std::string &language_name)
Converts seed words to bytes (secret key).
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
const std::string old_language_name
bool bytes_to_words(const char *src, size_t len, epee::wipeable_string &words, const std::string &language_name)
Converts bytes (secret key) to seed words.
POD_CLASS hash8
Definition: hash.h:53
void rand(size_t N, uint8_t *bytes)
Definition: crypto.h:207
POD_CLASS key_image
Definition: crypto.h:102
POD_CLASS hash
Definition: hash.h:50
const command_line::arg_descriptor< std::string, false, true, 2 > arg_data_dir
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
crypto::hash get_transaction_hash(const transaction &t)
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
blobdata tx_to_blob(const transaction &tx)
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
std::string blobdata
Definition: blobdatatype.h:39
std::string print_etn(uint64_t amount, unsigned int decimal_point)
bool get_account_address_from_str_or_url(address_parse_info &info, network_type nettype, const std::string &str_or_url, std::function< std::string(const std::string &, const std::vector< std::string > &, bool)> dns_confirm)
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
bool daemonize(int argc, char const *argv[], T_executor &&executor, boost::program_options::variables_map const &vm)
void init_options(boost::program_options::options_description &hidden_options, boost::program_options::options_description &normal_options)
epee::misc_utils::struct_init< response_t > response
std::string to_string(t_connection_type type)
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
Definition: net_ssl.cpp:516
std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len)
bool parse_hexstr_to_binbuff(const epee::span< const char > s, epee::span< char > &res)
Definition: string_tools.h:92
std::string pod_to_hex(const t_pod_type &s)
Definition: string_tools.h:317
bool hex_to_pod(const std::string &hex_str, t_pod_type &s)
Definition: string_tools.h:324
std::string buff_to_hex_nodelimer(const std::string &src)
Definition: string_tools.h:87
void set_console_color(int color, bool bright)
Definition: mlog.cpp:341
T & unwrap(mlocked< T > &src)
Definition: mlocker.h:80
void reset_console_color()
Definition: mlog.cpp:460
@ console_color_default
Definition: misc_log_ex.h:208
@ console_color_white
Definition: misc_log_ex.h:209
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
Definition: protocol.cpp:1038
error
Tracks LMDB error codes.
Definition: error.h:45
::std::string string
Definition: gtest-port.h:1097
const T & move(const T &t)
Definition: gtest-port.h:1317
file_error_base< file_exists_message_index > file_exists
Various Tools.
Definition: tools.cpp:31
unsigned get_max_concurrency()
Definition: util.cpp:868
boost::filesystem::path data_dir
Definition: main.cpp:50
const char * tr(const char *str)
Definition: wallet_args.cpp:81
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
command_line::arg_descriptor< std::string > arg_generate_from_json()
Definition: wallet_args.cpp:72
command_line::arg_descriptor< std::string > arg_wallet_file()
Definition: wallet_args.cpp:76
CXA_THROW_INFO_T void(* dest)(void *))
Definition: stack_trace.cpp:91
CXA_THROW_INFO_T * info
Definition: stack_trace.cpp:91
#define false
Definition: stdbool.h:38
unsigned int uint32_t
Definition: stdint.h:126
unsigned char uint8_t
Definition: stdint.h:124
unsigned __int64 uint64_t
Definition: stdint.h:136
static boost::optional< rpc_args > process(const boost::program_options::variables_map &vm, const bool any_cert_option=false)
Definition: rpc_args.cpp:125
static void init_options(boost::program_options::options_description &desc, const bool any_cert_option=false)
Definition: rpc_args.cpp:108
bool is_subaddress
bool is_integrated
std::string original
uint64_t amount
account_public_address addr
static epee::wipeable_string wipeable_string(const span< const std::uint8_t > src)
Definition: hex.cpp:69
static std::string string(const span< const std::uint8_t > src)
Definition: hex.cpp:68
std::set< uint32_t > m_subaddr_indices
Definition: wallet2.h:405
std::vector< cryptonote::tx_destination_entry > m_dests
Definition: wallet2.h:400
std::unordered_set< crypto::public_key > m_signers
Definition: wallet2.h:507
std::vector< pending_tx > m_ptx
Definition: wallet2.h:506
cryptonote::subaddress_index m_subaddr_index
Definition: wallet2.h:362
crypto::secret_key tx_key
Definition: wallet2.h:466
std::vector< cryptonote::tx_destination_entry > dests
Definition: wallet2.h:468
cryptonote::transaction tx
Definition: wallet2.h:460
std::vector< crypto::secret_key > additional_tx_keys
Definition: wallet2.h:467
std::vector< uint8_t > extra
Definition: wallet2.h:420
std::vector< cryptonote::tx_source_entry > sources
Definition: wallet2.h:416
std::vector< cryptonote::tx_destination_entry > splitted_dsts
Definition: wallet2.h:418
cryptonote::tx_destination_entry change_dts
Definition: wallet2.h:417
enum tools::wallet2::unconfirmed_transfer_details::@62 m_state
cryptonote::transaction_prefix m_tx
Definition: wallet2.h:380
std::vector< cryptonote::tx_destination_entry > m_dests
Definition: wallet2.h:385
std::vector< tx_construction_data > txes
Definition: wallet2.h:493
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< request_t > request
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< response_t > response
epee::misc_utils::struct_init< request_t > request
std::string note
std::string payment_id
bool double_spend_seen
std::string type
std::string txid
uint64_t height
uint64_t unlock_time
bool nonexistent_utxo_seen
uint64_t timestamp
uint64_t amount
cryptonote::subaddress_index subaddr_index
std::string address
uint64_t fee
uint64_t confirmations
uint64_t suggested_confirmations_threshold
std::list< transfer_destination > destinations
std::vector< cryptonote::subaddress_index > subaddr_indices
const char * address
Definition: multisig.cpp:37
const char * spendkey
Definition: multisig.cpp:38
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
int main(int argc, char **argv)
#define MKDIR(path, mode)
#define DEFAULT_AUTO_REFRESH_PERIOD
#define WALLET_RPC_VERSION
#define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC
#define WALLET_RPC_ERROR_CODE_TX_TOO_LARGE
#define WALLET_RPC_ERROR_CODE_BAD_HEX
#define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION
#define WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE
#define WALLET_RPC_ERROR_CODE_TRANSFER_TYPE
#define WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED
#define WALLET_RPC_ERROR_CODE_WATCH_ONLY
#define WALLET_RPC_ERROR_CODE_BAD_TX_METADATA
#define WALLET_RPC_ERROR_CODE_INVALID_PASSWORD
#define WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS
#define WALLET_RPC_ERROR_CODE_MULTISIG_SIGNATURE
#define WALLET_RPC_ERROR_CODE_WRONG_INDEX
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_UNLOCKED_ETN
#define WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID
#define WALLET_RPC_ERROR_CODE_NO_TXKEY
#define WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION
#define WALLET_RPC_ERROR_CODE_NOT_MULTISIG
#define WALLET_RPC_ERROR_CODE_ZERO_DESTINATION
#define WALLET_RPC_ERROR_CODE_NOT_OPEN
#define WALLET_RPC_ERROR_CODE_INVALID_LOG_LEVEL
#define WALLET_RPC_ERROR_CODE_WRONG_TXID
#define WALLET_RPC_ERROR_CODE_WRONG_KEY
#define WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE
#define WALLET_RPC_ERROR_CODE_WRONG_ADDRESS
#define WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUT_OF_BOUNDS
#define WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA
#define WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY
#define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR
#define WALLET_RPC_ERROR_CODE_NO_WALLET_DIR
#define WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA
#define WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS
#define WALLET_RPC_ERROR_CODE_DENIED
#define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_ETN
#define WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_OUTS_TO_MIX
#define WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG
#define WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA
#define WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR
#define WALLET_RPC_ERROR_CODE_WRONG_URI
#define WALLET_RPC_ERROR_CODE_MULTISIG_SUBMISSION