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>
58 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
59 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.rpc"
61 #define DEFAULT_AUTO_REFRESH_PERIOD 20
73 constexpr
const char default_rpc_username[] =
"electroneum";
75 boost::optional<tools::password_container> password_prompter(
const char *prompt,
bool verify)
80 MERROR(
"failed to read wallet password");
87 if (entry.
height >= blockchain_height || (entry.
height == 0 && (!strcmp(entry.
type.c_str(),
"pending") || !strcmp(entry.
type.c_str(),
"pool"))))
92 if (block_reward == 0)
94 else if (entry.
type ==
"migration")
109 wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(),
m_stop(
false), m_restricted(
false), m_vm(NULL)
128 if (m_auto_refresh_period == 0)
130 if (boost::posix_time::microsec_clock::universal_time() < m_last_auto_refresh_time + boost::posix_time::seconds(m_auto_refresh_period))
134 }
catch (
const std::exception& ex) {
135 LOG_ERROR(
"Exception at while refreshing, what=" << ex.what());
137 m_last_auto_refresh_time = boost::posix_time::microsec_clock::universal_time();
141 if (m_stop.load(std::memory_order_relaxed))
174 boost::optional<epee::net_utils::http::login> http_login{};
187 #define MKDIR(path, mode) mkdir(path)
189 #define MKDIR(path, mode) mkdir(path, mode)
191 if (!m_wallet_dir.empty() &&
MKDIR(m_wallet_dir.c_str(), 0700) < 0 && errno != EEXIST)
194 LOG_ERROR(
tr(
"Failed to create directory ") + m_wallet_dir);
196 LOG_ERROR((boost::format(
tr(
"Failed to create directory %s: %s")) % m_wallet_dir % strerror(errno)).str());
204 if (rpc_config->login)
207 LOG_ERROR(
tr(
"Cannot specify --") << arg_disable_rpc_login.name <<
tr(
" and --") << arg.rpc_login.name);
213 if (!rpc_config->login)
215 std::array<std::uint8_t, 16> rand_128bit{{}};
218 default_rpc_username,
222 std::string temp =
"electroneum-wallet-rpc." + bind_port +
".login";
224 if (!rpc_login_file.
handle())
226 LOG_ERROR(
tr(
"Failed to create file ") << temp <<
tr(
". Check permissions or remove file"));
229 std::fputs(http_login->username.c_str(), rpc_login_file.
handle());
230 std::fputc(
':', rpc_login_file.
handle());
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()))
239 LOG_PRINT_L0(
tr(
"RPC username/password is stored in file ") << temp);
244 std::move(rpc_config->login->username),
std::move(rpc_config->login->password).password()
247 assert(
bool(http_login));
251 m_last_auto_refresh_time = boost::posix_time::min_date_time;
253 check_background_mining();
263 void wallet_rpc_server::check_background_mining()
277 MDEBUG(
"Using an untrusted daemon, skipping background mining check");
286 MERROR(
"Failed to query mining status: " << (r ?
res.status :
"No connection to daemon"));
289 if (
res.active ||
res.is_background_mining_enabled)
304 req2.threads_count = 1;
305 req2.do_background_mining =
true;
306 req2.ignore_battery =
false;
328 if (entry.
payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
347 if (entry.
payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
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;
380 if (entry.
payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
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;
396 entry.
type = is_failed ?
"failed" :
"pending";
409 if (entry.
payment_id.substr(16).find_first_not_of(
'0') == std::string::npos)
428 if (!m_wallet)
return not_open(er);
432 res.balance = req.all_accounts ? m_wallet->
balance_all(syncedV10) : m_wallet->
balance(req.account_index, syncedV10);
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)
441 balance_per_subaddress_per_account[account_index] = m_wallet->
balance_per_subaddress(account_index, syncedV10);
447 balance_per_subaddress_per_account[req.account_index] = m_wallet->
balance_per_subaddress(req.account_index, syncedV10);
450 std::vector<tools::wallet2::transfer_details> transfers;
452 for (
const auto& p : balance_per_subaddress_per_account)
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())
460 address_indices = req.address_indices;
464 for (
const auto& i : balance_per_subaddress)
465 address_indices.insert(i.first);
469 wallet_rpc::COMMAND_RPC_GET_BALANCE::per_subaddress_info
info;
470 info.account_index = account_index;
471 info.address_index = i;
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;
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); });
483 catch (
const std::exception& e)
493 if (!m_wallet)
return not_open(er);
497 res.addresses.clear();
498 std::vector<uint32_t> req_address_index;
499 if (req.address_index.empty())
502 req_address_index.push_back(i);
506 req_address_index = req.address_index;
510 for (
uint32_t i : req_address_index)
513 res.addresses.resize(
res.addresses.size() + 1);
514 auto&
info =
res.addresses.back();
523 catch (
const std::exception& e)
533 if (!m_wallet)
return not_open(er);
538 er.
message =
"Invalid address";
545 er.
message =
"Address doesn't belong to the wallet";
554 if (!m_wallet)
return not_open(er);
561 catch (
const std::exception& e)
571 if (!m_wallet)
return not_open(er);
576 catch (
const std::exception& e)
586 if (!m_wallet)
return not_open(er);
590 res.total_balance = 0;
591 res.total_unlocked_balance = 0;
597 if(!req.account_index.empty()) {
598 subaddr_index.
major =
static_cast<uint32_t>(std::stoul(req.account_index));
600 wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info
info;
601 info.account_index = subaddr_index.
major;
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;
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)
618 er.
message = (boost::format(
tr(
"Tag %s is unregistered.")) % req.tag).str();
623 if (!req.tag.empty() && req.tag != account_tags.second[subaddr_index.
major])
625 wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info
info;
626 info.account_index = subaddr_index.
major;
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;
638 catch (
const std::exception& e)
648 if (!m_wallet)
return not_open(er);
655 catch (
const std::exception& e)
665 if (!m_wallet)
return not_open(er);
670 catch (
const std::exception& e)
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)
684 res.account_tags.resize(
res.account_tags.size() + 1);
685 auto&
info =
res.account_tags.back();
687 info.label = p.second;
688 for (
size_t i = 0; i < account_tags.second.size(); ++i)
690 if (account_tags.second[i] ==
info.tag)
691 info.accounts.push_back(i);
699 if (!m_wallet)
return not_open(er);
704 catch (
const std::exception& e)
714 if (!m_wallet)
return not_open(er);
719 catch (
const std::exception& e)
729 if (!m_wallet)
return not_open(er);
734 catch (
const std::exception& e)
744 if (!m_wallet)
return not_open(er);
749 catch (
const std::exception& e)
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)
761 for (
auto it = destinations.begin(); it != destinations.end(); it++)
767 [&er](
const std::string &url,
const std::vector<std::string> &addresses,
bool dnssec_valid)->std::string {
770 er.message = std::string(
"Invalid DNSSEC for ") + url;
773 if (addresses.empty())
775 er.message = std::string(
"No Electroneum address found at ") + url;
794 if (
info.has_payment_id)
796 if (!payment_id.empty() || integrated_payment_id != crypto::null_hash8)
799 er.
message =
"A single payment id is allowed per transaction";
804 memcpy(payment_id.data,
info.payment_id.data, 8);
805 memset(payment_id.data + 8, 0, 24);
812 er.
message =
"Something went wrong with integrated payment_id.";
818 if (at_least_one_destination && dsts.empty())
821 er.
message =
"No destinations for this transfer";
825 if (!payment_id.empty())
835 if (wallet2::parse_long_payment_id(payment_id_str, long_payment_id)) {
840 er.
message =
"Payment id has invalid format: \"" + payment_id_str +
"\", expected 64 character string";
847 er.
message =
"Something went wrong with payment_id. Please check its format: \"" + payment_id_str +
"\", expected 64-character string";
857 std::ostringstream oss;
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(); }
873 template<
typename T,
typename V>
874 static bool fill(
T &where, V s)
876 if (is_error_value(s))
return false;
881 template<
typename T,
typename V>
882 static bool fill(std::list<T> &where, V s)
884 if (is_error_value(s))
return false;
896 template<
typename Ts,
typename Tu>
897 bool wallet_rpc_server::fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
899 Ts &tx_hash,
bool get_tx_hex, Ts &tx_blob,
bool get_tx_metadata, Ts &tx_metadata,
epee::json_rpc::error &er)
901 for (
const auto & ptx : ptx_vector)
911 fill(amount, total_amount(ptx));
915 if (m_wallet->multisig())
918 if (multisig_txset.empty())
921 er.
message =
"Failed to save multisig tx set after creation";
927 if (m_wallet->watch_only()){
929 if (unsigned_txset.empty())
932 er.
message =
"Failed to save unsigned tx set after creation";
936 else if (!do_not_relay)
937 m_wallet->commit_tx(ptx_vector);
940 for (
auto & ptx : ptx_vector)
944 r = r && (!get_tx_metadata || fill(tx_metadata, ptx_to_string(ptx)));
948 er.
message =
"Failed to save tx info";
959 std::vector<cryptonote::tx_destination_entry> dsts;
960 std::vector<uint8_t> extra;
963 if (!m_wallet)
return not_open(er);
967 er.
message =
"Command unavailable in restricted mode.";
972 if (!validate_transfer(req.destinations, req.payment_id, dsts, extra,
true, er))
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);
983 if (ptx_vector.empty())
986 er.
message =
"No transaction created";
991 if (ptx_vector.size() != 1)
994 er.
message =
"Transaction would be too large. try /transfer_split.";
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);
1001 catch (
const std::exception& e)
1012 std::vector<cryptonote::tx_destination_entry> dsts;
1013 std::vector<uint8_t> extra;
1015 if (!m_wallet)
return not_open(er);
1019 er.
message =
"Command unavailable in restricted mode.";
1024 if (!validate_transfer(req.destinations, req.payment_id, dsts, extra,
true, er))
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");
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);
1040 catch (
const std::exception& e)
1050 if (!m_wallet)
return not_open(er);
1054 er.
message =
"Command unavailable in restricted mode.";
1057 if (m_wallet->key_on_device())
1060 er.
message =
"command not supported by HW wallet";
1063 if(m_wallet->watch_only())
1066 er.
message =
"command not supported by watch-only wallet";
1074 er.
message =
"Failed to parse hex.";
1079 if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs))
1082 er.
message =
"cannot load unsigned_txset";
1086 std::vector<tools::wallet2::pending_tx> ptxs;
1090 std::string ciphertext = m_wallet->sign_tx_dump_to_str(exported_txs, ptxs, signed_txs);
1091 if (ciphertext.empty())
1094 er.
message =
"Failed to sign unsigned tx";
1100 catch (
const std::exception &e)
1107 for (
auto &ptx: ptxs)
1110 if (req.get_tx_keys)
1120 for (
auto &ptx: ptxs)
1131 if (!m_wallet)
return not_open(er);
1135 er.
message =
"Command unavailable in restricted mode.";
1138 if (m_wallet->key_on_device())
1141 er.
message =
"command not supported by HW wallet";
1144 if(m_wallet->watch_only())
1147 er.
message =
"command not supported by watch-only wallet";
1150 if(req.unsigned_txset.empty() && req.multisig_txset.empty())
1153 er.
message =
"no txset provided";
1157 std::vector <wallet2::tx_construction_data> tx_constructions;
1158 if (!req.unsigned_txset.empty()) {
1164 er.
message =
"Failed to parse hex.";
1167 if (!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs)) {
1169 er.
message =
"cannot load unsigned_txset";
1172 tx_constructions = exported_txs.
txes;
1174 catch (
const std::exception &e) {
1179 }
else if (!req.multisig_txset.empty()) {
1185 er.
message =
"Failed to parse hex.";
1188 if (!m_wallet->parse_multisig_tx_from_str(blob, exported_txs)) {
1190 er.
message =
"cannot load multisig_txset";
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);
1198 catch (
const std::exception &e) {
1205 std::vector<tools::wallet2::pending_tx> ptx;
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)
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();
1217 std::vector<cryptonote::tx_extra_field> tx_extra_fields;
1218 bool has_encrypted_payment_id =
false;
1229 memcpy(payment_id.data, payment_id8.data, 8);
1230 memset(payment_id.data + 8, 0, 24);
1241 for (
size_t s = 0; s < cd.
sources.size(); ++s)
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;
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)));
1258 i->second.second += entry.
amount;
1259 desc.amount_out += entry.
amount;
1264 if (it == dests.end())
1267 er.
message =
"Claimed change does not go to a paid address";
1273 er.
message =
"Claimed change is larger than payment to the change address";
1278 if (first_known_non_zero_change_index == -1)
1279 first_known_non_zero_change_index = n;
1284 er.
message =
"Change goes to more than one address";
1290 if (it->second.second == 0)
1294 size_t n_dummy_outputs = 0;
1295 for (
auto i = dests.begin(); i != dests.end(); )
1297 if (i->second.second > 0)
1299 desc.recipients.push_back({i->second.first, i->second.second});
1302 ++desc.dummy_outputs;
1306 if (desc.change_amount > 0)
1312 desc.fee = desc.amount_in - desc.amount_out;
1317 catch (
const std::exception &e)
1320 er.
message =
"failed to parse unsigned transfers";
1329 if (!m_wallet)
return not_open(er);
1333 er.
message =
"Command unavailable in restricted mode.";
1336 if (m_wallet->key_on_device())
1339 er.
message =
"command not supported by HW wallet";
1347 er.
message =
"Failed to parse hex.";
1351 std::vector<tools::wallet2::pending_tx> ptx_vector;
1354 bool r = m_wallet->parse_tx_from_str(blob, ptx_vector, NULL);
1358 er.
message =
"Failed to parse signed tx data.";
1362 catch (
const std::exception &e)
1371 for (
auto &ptx: ptx_vector)
1373 m_wallet->commit_tx(ptx);
1377 catch (
const std::exception &e)
1389 if (!m_wallet)
return not_open(er);
1393 er.
message =
"Command unavailable in restricted mode.";
1399 std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions();
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);
1404 catch (
const std::exception& e)
1414 std::vector<cryptonote::tx_destination_entry> dsts;
1415 std::vector<uint8_t> extra;
1417 if (!m_wallet)
return not_open(er);
1421 er.
message =
"Command unavailable in restricted mode.";
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))
1435 if (req.outputs < 1)
1438 er.
message =
"Amount of outputs should be greater than 0.";
1442 std::set<uint32_t> subaddr_indices;
1443 if (req.subaddr_indices_all)
1445 for (
uint32_t i = 0; i < m_wallet->get_num_subaddresses(req.account_index); ++i)
1446 subaddr_indices.insert(i);
1450 subaddr_indices= req.subaddr_indices;
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);
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);
1462 catch (
const std::exception& e)
1472 std::vector<cryptonote::tx_destination_entry> dsts;
1473 std::vector<uint8_t> extra;
1475 if (!m_wallet)
return not_open(er);
1479 er.
message =
"Command unavailable in restricted mode.";
1483 if (req.outputs < 1)
1486 er.
message =
"Amount of outputs should be greater than 0.";
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))
1504 er.
message =
"failed to parse key image";
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);
1514 if (ptx_vector.empty())
1517 er.
message =
"No outputs found";
1520 if (ptx_vector.size() > 1)
1523 er.
message =
"Multiple transactions are created, which is not supposed to happen";
1526 const wallet2::pending_tx &ptx = ptx_vector[0];
1527 if (ptx.selected_transfers.size() > 1)
1530 er.
message =
"The transaction uses multiple inputs, which is not supposed to happen";
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);
1537 catch (
const std::exception& e)
1545 er.
message =
"WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
1553 if (!m_wallet)
return not_open(er);
1559 er.
message =
"Failed to parse hex.";
1566 std::istringstream iss(blob);
1573 er.
message =
"Failed to parse tx metadata.";
1579 m_wallet->commit_tx(ptx);
1581 catch(
const std::exception &e)
1584 er.
message =
"Failed to commit tx.";
1595 if (!m_wallet)
return not_open(er);
1599 if (req.payment_id.empty())
1601 payment_id = crypto::rand<crypto::hash8>();
1608 er.
message =
"Invalid payment ID";
1613 if (req.standard_address.empty())
1615 res.integrated_address = m_wallet->get_integrated_address_as_str(payment_id);
1623 er.
message =
"Invalid address";
1626 if (
info.is_subaddress)
1629 er.
message =
"Subaddress shouldn't be used";
1632 if (
info.has_payment_id)
1635 er.
message =
"Already integrated address";
1638 if (req.payment_id.empty())
1641 er.
message =
"Payment ID shouldn't be left unspecified";
1649 catch (
const std::exception& e)
1659 if (!m_wallet)
return not_open(er);
1667 er.
message =
"Invalid address";
1670 if(!
info.has_payment_id)
1673 er.
message =
"Address is not an integrated address";
1680 catch (
const std::exception& e)
1690 if (!m_wallet)
return not_open(er);
1694 er.
message =
"Command unavailable in restricted mode.";
1702 catch (
const std::exception& e)
1712 if (!m_wallet)
return not_open(er);
1719 er.
message =
"Payment ID has invalid format";
1723 if(
sizeof(payment_id) == payment_id_blob.size())
1725 payment_id = *
reinterpret_cast<const crypto::hash*
>(payment_id_blob.data());
1727 else if(
sizeof(payment_id8) == payment_id_blob.size())
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);
1736 er.
message =
"Payment ID has invalid size: " + req.payment_id;
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)
1745 wallet_rpc::payment_details rpc_payment;
1746 rpc_payment.payment_id = req.payment_id;
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);
1761 res.payments.clear();
1762 if (!m_wallet)
return not_open(er);
1765 if (req.payment_ids.empty())
1767 std::list<std::pair<crypto::hash,wallet2::payment_details>> payment_list;
1768 m_wallet->get_payments(payment_list, req.min_block_height);
1770 for (
auto & payment : payment_list)
1772 wallet_rpc::payment_details rpc_payment;
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);
1786 for (
auto & payment_id_str : req.payment_ids)
1794 if (payment_id_str.size() == 2 *
sizeof(payment_id))
1798 else if (payment_id_str.size() == 2 *
sizeof(payment_id8))
1803 memcpy(payment_id.data, payment_id8.data, 8);
1804 memset(payment_id.data + 8, 0, 24);
1810 er.
message =
"Payment ID has invalid size: " + payment_id_str;
1817 er.
message =
"Payment ID has invalid format: " + payment_id_str;
1821 std::list<wallet2::payment_details> payment_list;
1822 m_wallet->get_payments(payment_id, payment_list, req.min_block_height);
1824 for (
auto & payment : payment_list)
1826 wallet_rpc::payment_details rpc_payment;
1827 rpc_payment.payment_id = payment_id_str;
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);
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)
1847 er.
message =
"Transfer type must be one of: all, available, or unavailable";
1851 bool filter =
false;
1852 bool available =
false;
1853 if (req.transfer_type.compare(
"available") == 0)
1858 else if (req.transfer_type.compare(
"unavailable") == 0)
1864 wallet2::transfer_container transfers;
1865 m_wallet->get_transfers(transfers);
1867 bool transfers_found =
false;
1868 for (
const auto& td : transfers)
1870 if (!filter || available != td.m_spent)
1872 if (req.account_index != td.m_subaddr_index.major || (!req.subaddr_indices.empty() && req.subaddr_indices.count(td.m_subaddr_index.minor) == 0))
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;
1880 rpc_transfers.subaddr_index = {td.m_subaddr_index.major, td.m_subaddr_index.minor};
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);
1894 if (!m_wallet)
return not_open(er);
1898 er.
message =
"Command unavailable in restricted mode.";
1902 if (req.key_type.compare(
"mnemonic") == 0)
1906 if (m_wallet->multisig(&ready))
1911 er.
message =
"This wallet is multisig, but not yet finalized";
1914 if (!m_wallet->get_multisig_seed(seed))
1917 er.
message =
"Failed to get multisig seed.";
1923 if (m_wallet->watch_only())
1926 er.
message =
"The wallet is watch-only. Cannot retrieve seed.";
1929 if (!m_wallet->is_deterministic())
1932 er.
message =
"The wallet is non-deterministic. Cannot display seed.";
1935 if (!m_wallet->get_seed(seed))
1938 er.
message =
"Failed to get seed.";
1944 else if(req.key_type.compare(
"view_key") == 0)
1949 else if(req.key_type.compare(
"spend_key") == 0)
1951 if (m_wallet->watch_only())
1954 er.
message =
"The wallet is watch-only. Cannot retrieve spend key.";
1962 er.
message =
"key_type " + req.key_type +
" not found";
1971 if (!m_wallet)
return not_open(er);
1975 er.
message =
"Command unavailable in restricted mode.";
1981 m_wallet->rescan_blockchain(req.hard);
1983 catch (
const std::exception& e)
1993 if (!m_wallet)
return not_open(er);
1997 er.
message =
"Command unavailable in restricted mode.";
2001 res.signature = m_wallet->sign(req.data);
2007 if (!m_wallet)
return not_open(er);
2011 er.
message =
"Command unavailable in restricted mode.";
2018 [&er](
const std::string &url,
const std::vector<std::string> &addresses,
bool dnssec_valid)->std::string {
2021 er.message = std::string(
"Invalid DNSSEC for ") + url;
2024 if (addresses.empty())
2026 er.message = std::string(
"No ETN address found at ") + url;
2029 return addresses[0];
2036 res.good = m_wallet->verify(req.data,
info.address, req.signature);
2042 if (!m_wallet)
return not_open(er);
2046 er.
message =
"Command unavailable in restricted mode.";
2053 m_stop.store(
true, std::memory_order_relaxed);
2055 catch (
const std::exception& e)
2065 if (!m_wallet)
return not_open(er);
2069 er.
message =
"Command unavailable in restricted mode.";
2073 if (req.txids.size() != req.notes.size())
2076 er.
message =
"Different amount of txids and notes";
2080 std::list<crypto::hash> txids;
2081 std::list<std::string>::const_iterator i = req.txids.begin();
2082 while (i != req.txids.end())
2088 er.
message =
"TX ID has invalid format";
2093 txids.push_back(txid);
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())
2100 m_wallet->set_tx_note(*il++, *in++);
2109 if (!m_wallet)
return not_open(er);
2111 std::list<crypto::hash> txids;
2112 std::list<std::string>::const_iterator i = req.txids.begin();
2113 while (i != req.txids.end())
2119 er.
message =
"TX ID has invalid format";
2124 txids.push_back(txid);
2127 std::list<crypto::hash>::const_iterator il = txids.begin();
2128 while (il != txids.end())
2130 res.notes.push_back(m_wallet->get_tx_note(*il++));
2137 if (!m_wallet)
return not_open(er);
2141 er.
message =
"Command unavailable in restricted mode.";
2145 m_wallet->set_attribute(req.key, req.value);
2152 if (!m_wallet)
return not_open(er);
2156 er.
message =
"Command unavailable in restricted mode.";
2160 res.value = m_wallet->get_attribute(req.key);
2165 if (!m_wallet)
return not_open(er);
2171 er.
message =
"TX ID has invalid format";
2176 std::vector<crypto::secret_key> additional_tx_keys;
2177 if (!m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
2180 er.
message =
"No tx secret key is stored for this tx";
2186 for (
size_t i = 0; i < additional_tx_keys.size(); ++i)
2194 if (!m_wallet)
return not_open(er);
2200 er.
message =
"TX ID has invalid format";
2205 if (tx_key_str.
size() < 64 || tx_key_str.
size() % 64)
2208 er.
message =
"Tx key has invalid format";
2211 const char *data = tx_key_str.
data();
2216 er.
message =
"Tx key has invalid format";
2220 std::vector<crypto::secret_key> additional_tx_keys;
2221 while (offset < tx_key_str.
size())
2223 additional_tx_keys.resize(additional_tx_keys.size() + 1);
2227 er.
message =
"Tx key has invalid format";
2237 er.
message =
"Invalid address";
2243 m_wallet->check_tx_key(txid, tx_key, additional_tx_keys,
info.address,
res.received,
res.in_pool,
res.confirmations);
2245 catch (
const std::exception &e)
2256 if (!m_wallet)
return not_open(er);
2262 er.
message =
"TX ID has invalid format";
2270 er.
message =
"Invalid address";
2276 res.signature = m_wallet->get_tx_proof(txid,
info.address,
info.is_subaddress, req.message);
2278 catch (
const std::exception &e)
2289 if (!m_wallet)
return not_open(er);
2295 er.
message =
"TX ID has invalid format";
2303 er.
message =
"Invalid address";
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);
2311 catch (
const std::exception &e)
2322 if (!m_wallet)
return not_open(er);
2328 er.
message =
"TX ID has invalid format";
2334 res.signature = m_wallet->get_spend_proof(txid, req.message);
2336 catch (
const std::exception &e)
2347 if (!m_wallet)
return not_open(er);
2353 er.
message =
"TX ID has invalid format";
2359 res.good = m_wallet->check_spend_proof(txid, req.message, req.signature);
2361 catch (
const std::exception &e)
2372 if (!m_wallet)
return not_open(er);
2374 boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
2377 if (req.account_index >= m_wallet->get_num_subaddress_accounts())
2380 er.
message =
"Account index is out of bound";
2383 account_minreserve = std::make_pair(req.account_index, req.amount);
2388 res.signature = m_wallet->get_reserve_proof(account_minreserve, req.message);
2390 catch (
const std::exception &e)
2401 if (!m_wallet)
return not_open(er);
2407 er.
message =
"Invalid address";
2410 if (
info.is_subaddress)
2413 er.
message =
"Address must not be a subaddress";
2419 res.good = m_wallet->check_reserve_proof(
info.address, req.message, req.signature,
res.total,
res.spent);
2421 catch (
const std::exception &e)
2432 if (!m_wallet)
return not_open(er);
2436 er.
message =
"Command unavailable in restricted mode.";
2441 if (req.filter_by_height)
2443 min_height = req.min_height;
2444 max_height = req.max_height <= max_height ? req.max_height : max_height;
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)
2451 account_index = boost::none;
2452 subaddr_indices.clear();
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);
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);
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);
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) {
2491 if (!((req.failed && is_failed) || (!is_failed && req.pending)))
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);
2501 m_wallet->update_pool_state();
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);
2516 if (!m_wallet)
return not_open(er);
2520 er.
message =
"Command unavailable in restricted mode.";
2529 er.
message =
"Transaction ID has invalid format";
2533 if(
sizeof(txid) == txid_blob.size())
2535 txid = *
reinterpret_cast<const crypto::hash*
>(txid_blob.data());
2540 er.
message =
"Transaction ID has invalid size: " + req.txid;
2544 if (req.account_index >= m_wallet->get_num_subaddress_accounts())
2547 er.
message =
"Account index is out of bound";
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)
2556 res.transfers.resize(
res.transfers.size() + 1);
2557 fill_transfer_entry(
res.transfers.back(), i->second.m_tx_hash, i->first, i->second);
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)
2566 res.transfers.resize(
res.transfers.size() + 1);
2567 fill_transfer_entry(
res.transfers.back(), i->first, i->second);
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)
2576 res.transfers.resize(
res.transfers.size() + 1);
2577 fill_transfer_entry(
res.transfers.back(), i->first, i->second);
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)
2586 res.transfers.resize(
res.transfers.size() + 1);
2587 fill_transfer_entry(
res.transfers.back(), i->first, i->second);
2591 m_wallet->update_pool_state();
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)
2598 res.transfers.resize(
res.transfers.size() + 1);
2599 fill_transfer_entry(
res.transfers.back(), i->first, i->second);
2603 if (!
res.transfers.empty())
2605 res.transfer =
res.transfers.front();
2610 er.
message =
"Transaction not found.";
2616 if (!m_wallet)
return not_open(er);
2620 er.
message =
"Command unavailable in restricted mode.";
2623 if (m_wallet->key_on_device())
2626 er.
message =
"command not supported by HW wallet";
2634 catch (
const std::exception &e)
2645 if (!m_wallet)
return not_open(er);
2649 er.
message =
"Command unavailable in restricted mode.";
2652 if (m_wallet->key_on_device())
2655 er.
message =
"command not supported by HW wallet";
2663 er.
message =
"Failed to parse hex.";
2669 res.num_imported = m_wallet->import_outputs_from_str(blob);
2671 catch (
const std::exception &e)
2682 if (!m_wallet)
return not_open(er);
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)
2695 catch (
const std::exception& e)
2706 if (!m_wallet)
return not_open(er);
2710 er.
message =
"Command unavailable in restricted mode.";
2713 if (!m_wallet->is_trusted_daemon())
2716 er.
message =
"This command requires a trusted daemon.";
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)
2728 er.
message =
"failed to parse key image";
2735 er.
message =
"failed to parse signature";
2740 uint64_t height = m_wallet->import_key_images(ski, req.offset, spent, unspent);
2742 res.unspent = unspent;
2746 catch (
const std::exception& e)
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);
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))
2786 if (!m_wallet)
return not_open(er);
2787 const auto ab = m_wallet->get_address_book();
2788 if (req.entries.empty())
2791 for (
const auto &entry: ab)
2798 if (idx >= ab.size())
2804 const auto &entry = ab[idx];
2813 if (!m_wallet)
return not_open(er);
2817 er.
message =
"Command unavailable in restricted mode.";
2825 [&er](
const std::string &url,
const std::vector<std::string> &addresses,
bool dnssec_valid)->std::string {
2828 er.message = std::string(
"Invalid DNSSEC for ") + url;
2831 if (addresses.empty())
2833 er.message = std::string(
"No ETN address found at ") + url;
2836 return addresses[0];
2844 if (
info.has_payment_id)
2846 memcpy(payment_id.data,
info.payment_id.data, 8);
2847 memset(payment_id.data + 8, 0, 24);
2849 if (!req.payment_id.empty())
2851 if (
info.has_payment_id)
2854 er.
message =
"Separate payment ID given with integrated address";
2860 if (!wallet2::parse_long_payment_id(req.payment_id, payment_id))
2862 if (!wallet2::parse_short_payment_id(req.payment_id,
info.payment_id))
2865 er.
message =
"Payment id has invalid format: \"" + req.payment_id +
"\", expected 64 character string";
2871 er.
message =
"Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address";
2876 if (!m_wallet->add_address_book_row(
info.address, payment_id, req.description,
info.is_subaddress))
2879 er.
message =
"Failed to add address book entry";
2882 res.index = m_wallet->get_address_book().size() - 1;
2888 if (!m_wallet)
return not_open(er);
2892 er.
message =
"Command unavailable in restricted mode.";
2896 const auto ab = m_wallet->get_address_book();
2897 if (req.index >= ab.size())
2903 if (!m_wallet->delete_address_book_row(req.index))
2906 er.
message =
"Failed to delete address book entry";
2914 if (!m_wallet)
return not_open(er);
2918 er.
message =
"Command unavailable in restricted mode.";
2923 m_wallet->refresh(m_wallet->is_trusted_daemon(), req.start_height,
res.blocks_fetched,
res.received_etn);
2926 catch (
const std::exception& e)
2939 er.
message =
"Command unavailable in restricted mode.";
2948 catch (
const std::exception& e)
2958 if (!m_wallet)
return not_open(er);
2962 er.
message =
"Command unavailable in restricted mode.";
2967 m_wallet->rescan_spent();
2970 catch (
const std::exception& e)
2980 if (!m_wallet)
return not_open(er);
2981 if (!m_wallet->is_trusted_daemon())
2984 er.
message =
"This command requires a trusted daemon.";
2989 if (req.threads_count < 1 || max_mining_threads_count < req.threads_count)
2992 er.
message =
"The specified number of threads is inappropriate.";
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;
3003 bool r = m_wallet->invoke_http_json(
"/start_mining", daemon_req, daemon_res);
3007 er.
message =
"Couldn't start mining due to unknown error.";
3015 if (!m_wallet)
return not_open(er);
3018 bool r = m_wallet->invoke_http_json(
"/stop_mining", daemon_req, daemon_res);
3022 er.
message =
"Couldn't stop mining due to unknown error.";
3037 if (m_wallet_dir.empty())
3040 er.
message =
"No wallet dir configured";
3044 namespace po = boost::program_options;
3045 po::variables_map vm2;
3046 const char *ptr = strchr(req.filename.c_str(),
'/');
3049 ptr = strchr(req.filename.c_str(),
'\\');
3051 ptr = strchr(req.filename.c_str(),
':');
3056 er.
message =
"Invalid filename";
3059 std::string wallet_file = req.filename.empty() ?
"" : (m_wallet_dir +
"/" + req.filename);
3061 std::vector<std::string> languages;
3063 std::vector<std::string>::iterator it;
3065 it = std::find(languages.begin(), languages.end(), req.language);
3066 if (it == languages.end())
3069 it = std::find(languages.begin(), languages.end(), req.language);
3071 if (it == languages.end())
3074 er.
message =
"Unknown language: " + req.language;
3079 po::options_description desc(
"dummy");
3081 const char *argv[4];
3083 argv[0] =
"wallet-rpc";
3084 argv[1] =
"--password";
3085 argv[2] = req.password.c_str();
3095 er.
message =
"Failed to create wallet";
3098 wal->set_seed_language(req.language);
3102 bool r = wal->invoke_http_json(
"/getheight", hreq, hres);
3104 wal->set_refresh_from_block_height(hres.height);
3107 wal->generate(wallet_file, req.password, dummy_key,
false,
false);
3109 catch (
const std::exception& e)
3117 er.
message =
"Failed to generate wallet";
3127 catch (
const std::exception& e)
3134 m_wallet = wal.release();
3140 if (m_wallet_dir.empty())
3143 er.
message =
"No wallet dir configured";
3147 namespace po = boost::program_options;
3148 po::variables_map vm2;
3149 const char *ptr = strchr(req.filename.c_str(),
'/');
3152 ptr = strchr(req.filename.c_str(),
'\\');
3154 ptr = strchr(req.filename.c_str(),
':');
3159 er.
message =
"Invalid filename";
3162 if (m_wallet && req.autosave_current)
3168 catch (
const std::exception& e)
3174 std::string wallet_file = m_wallet_dir +
"/" + req.filename;
3176 po::options_description desc(
"dummy");
3178 const char *argv[4];
3180 argv[0] =
"wallet-rpc";
3181 argv[1] =
"--password";
3182 argv[2] = req.password.c_str();
3188 std::unique_ptr<tools::wallet2> wal =
nullptr;
3192 catch (
const std::exception& e)
3199 er.
message =
"Failed to open wallet";
3205 m_wallet = wal.release();
3211 if (!m_wallet)
return not_open(er);
3213 if (req.autosave_current)
3219 catch (
const std::exception& e)
3232 if (!m_wallet)
return not_open(er);
3236 er.
message =
"Command unavailable in restricted mode.";
3239 if (m_wallet->verify_password(req.old_password))
3243 m_wallet->change_password(m_wallet->get_wallet_file(), req.old_password, req.new_password);
3246 catch (
const std::exception& e)
3255 er.
message =
"Invalid original password.";
3261 void wallet_rpc_server::handle_rpc_exception(
const std::exception_ptr& e,
epee::json_rpc::error& er,
int default_error_code) {
3264 std::rethrow_exception(e);
3294 er.
message = (boost::format(
tr(
"Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee)")) %
3309 er.
message =
"Cannot create wallet. Already exists.";
3311 catch (
const error::invalid_password& e)
3314 er.
message =
"Invalid password.";
3316 catch (
const error::account_index_outofbound& e)
3321 catch (
const error::address_index_outofbound& e)
3326 catch (
const error::signature_check_failed& e)
3331 catch (
const std::exception& e)
3333 er.
code = default_error_code;
3339 er.
message =
"WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
3345 if (m_wallet_dir.empty())
3348 er.
message =
"No wallet dir configured";
3353 if (req.viewkey.empty())
3356 er.
message =
"field 'viewkey' is mandatory. Please provide a view key you want to restore from.";
3359 if (req.address.empty())
3362 er.
message =
"field 'address' is mandatory. Please provide a public address.";
3366 namespace po = boost::program_options;
3367 po::variables_map vm2;
3368 const char *ptr = strchr(req.filename.c_str(),
'/');
3371 ptr = strchr(req.filename.c_str(),
'\\');
3373 ptr = strchr(req.filename.c_str(),
':');
3378 er.
message =
"Invalid filename";
3381 std::string wallet_file = req.filename.empty() ?
"" : (m_wallet_dir +
"/" + req.filename);
3383 if (!wallet_file.empty())
3387 boost::system::error_code ignored_ec;
3390 catch (
const std::exception &e)
3393 er.
message =
"Wallet already exists.";
3399 po::options_description desc(
"dummy");
3401 const char *argv[4];
3403 argv[0] =
"wallet-rpc";
3404 argv[1] =
"--password";
3405 argv[2] = req.password.c_str();
3413 std::unique_ptr<wallet2> wal;
3418 er.
message =
"Failed to create wallet";
3426 er.
message =
"Failed to parse public address";
3436 er.
message =
"Failed to parse view key secret key";
3440 if (m_wallet && req.autosave_current)
3444 if (!wallet_file.empty())
3447 catch (
const std::exception &e)
3456 if (!req.spendkey.empty())
3463 er.
message =
"Failed to parse spend key secret key";
3466 wal->generate(wallet_file,
std::move(rc.second).password(),
info.address,
spendkey, viewkey,
false);
3467 res.info =
"Wallet has been generated successfully.";
3471 wal->generate(wallet_file,
std::move(rc.second).password(),
info.address, viewkey,
false);
3472 res.info =
"Watch-only wallet has been generated successfully.";
3474 MINFO(
"Wallet has been generated.\n");
3476 catch (
const std::exception &e)
3485 er.
message =
"Failed to generate wallet";
3492 wal->set_refresh_from_block_height(req.restore_height);
3493 wal->rewrite(wallet_file, password);
3495 catch (
const std::exception &e)
3503 m_wallet = wal.release();
3504 res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3510 if (m_wallet_dir.empty())
3513 er.
message =
"No wallet dir configured";
3518 if (req.seed.empty())
3521 er.
message =
"field 'seed' is mandatory. Please provide a seed you want to restore from.";
3525 namespace po = boost::program_options;
3526 po::variables_map vm2;
3527 const char *ptr = strchr(req.filename.c_str(),
'/');
3530 ptr = strchr(req.filename.c_str(),
'\\');
3532 ptr = strchr(req.filename.c_str(),
':');
3537 er.
message =
"Invalid filename";
3540 std::string wallet_file = req.filename.empty() ?
"" : (m_wallet_dir +
"/" + req.filename);
3542 if (!wallet_file.empty())
3546 boost::system::error_code ignored_ec;
3549 catch (
const std::exception &e)
3552 er.
message =
"Wallet already exists.";
3564 er.
message =
"Electrum-style word list failed verification";
3568 if (m_wallet && req.autosave_current)
3574 catch (
const std::exception &e)
3583 if (!req.seed_offset.empty())
3589 po::options_description desc(
"dummy");
3591 const char *argv[4];
3593 argv[0] =
"wallet-rpc";
3594 argv[1] =
"--password";
3595 argv[2] = req.password.c_str();
3603 std::unique_ptr<wallet2> wal;
3608 er.
message =
"Failed to create wallet";
3618 if (was_deprecated_wallet)
3621 res.was_deprecated =
true;
3626 if (req.language.empty())
3629 er.
message =
"Wallet was using the old seed language. You need to specify a new seed language.";
3632 std::vector<std::string> language_list;
3633 std::vector<std::string> language_list_en;
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())
3640 er.
message =
"Wallet was using the old seed language, and the specified new seed language is invalid.";
3643 mnemonic_language = req.language;
3646 wal->set_seed_language(mnemonic_language);
3651 recovery_val = wal->generate(wallet_file,
std::move(rc.second).password(), recovery_key,
true,
false,
false);
3652 MINFO(
"Wallet has been restored.\n");
3654 catch (
const std::exception &e)
3665 er.
message =
"Failed to encode seed";
3673 er.
message =
"Failed to generate wallet";
3680 wal->set_refresh_from_block_height(req.restore_height);
3681 wal->rewrite(wallet_file, password);
3683 catch (
const std::exception &e)
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.";
3699 if (!m_wallet)
return not_open(er);
3700 res.multisig = m_wallet->multisig(&
res.ready, &
res.threshold, &
res.total);
3706 if (!m_wallet)
return not_open(er);
3710 er.
message =
"Command unavailable in restricted mode.";
3713 if (m_wallet->multisig())
3716 er.
message =
"This wallet is already multisig";
3719 if (m_wallet->watch_only())
3722 er.
message =
"wallet is watch-only and cannot be made multisig";
3726 res.multisig_info = m_wallet->get_multisig_info();
3732 if (!m_wallet)
return not_open(er);
3736 er.
message =
"Command unavailable in restricted mode.";
3739 if (m_wallet->multisig())
3742 er.
message =
"This wallet is already multisig";
3745 if (m_wallet->watch_only())
3748 er.
message =
"wallet is watch-only and cannot be made multisig";
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());
3757 catch (
const std::exception &e)
3769 if (!m_wallet)
return not_open(er);
3773 er.
message =
"Command unavailable in restricted mode.";
3777 if (!m_wallet->multisig(&ready))
3780 er.
message =
"This wallet is not multisig";
3786 er.
message =
"This wallet is multisig, but not yet finalized";
3793 info = m_wallet->export_multisig();
3795 catch (
const std::exception &e)
3809 if (!m_wallet)
return not_open(er);
3813 er.
message =
"Command unavailable in restricted mode.";
3818 if (!m_wallet->multisig(&ready, &
threshold, &total))
3821 er.
message =
"This wallet is not multisig";
3827 er.
message =
"This wallet is multisig, but not yet finalized";
3834 er.
message =
"Needs multisig export info from more participants";
3838 std::vector<cryptonote::blobdata>
info;
3839 info.resize(req.info.size());
3840 for (
size_t n = 0; n <
info.size(); ++n)
3845 er.
message =
"Failed to parse hex.";
3852 res.n_outputs = m_wallet->import_multisig(
info);
3854 catch (
const std::exception &e)
3857 er.
message =
"Error calling import_multisig";
3861 if (m_wallet->is_trusted_daemon())
3865 m_wallet->rescan_spent();
3867 catch (
const std::exception &e)
3869 er.
message =
std::string(
"Success, but failed to update spent status after import multisig info: ") + e.what();
3874 er.
message =
"Success, but cannot update spent status after import multisig info as daemon is untrusted";
3882 if (!m_wallet)
return not_open(er);
3886 er.
message =
"Command unavailable in restricted mode.";
3891 if (!m_wallet->multisig(&ready, &
threshold, &total))
3894 er.
message =
"This wallet is not multisig";
3900 er.
message =
"This wallet is multisig, and already finalized";
3904 if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
3907 er.
message =
"Needs multisig info from more participants";
3913 if (!m_wallet->finalize_multisig(req.password, req.multisig_info))
3916 er.
message =
"Error calling finalize_multisig";
3920 catch (
const std::exception &e)
3926 res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3933 if (!m_wallet)
return not_open(er);
3937 er.
message =
"Command unavailable in restricted mode.";
3942 if (!m_wallet->multisig(&ready, &
threshold, &total))
3945 er.
message =
"This wallet is not multisig";
3952 er.
message =
"This wallet is multisig, and already finalized";
3956 if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
3959 er.
message =
"Needs multisig info from more participants";
3965 res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info);
3966 if (
res.multisig_info.empty())
3968 res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
3971 catch (
const std::exception &e)
3982 if (!m_wallet)
return not_open(er);
3986 er.
message =
"Command unavailable in restricted mode.";
3991 if (!m_wallet->multisig(&ready, &
threshold, &total))
3994 er.
message =
"This wallet is not multisig";
4000 er.
message =
"This wallet is multisig, but not yet finalized";
4008 er.
message =
"Failed to parse hex.";
4013 bool r = m_wallet->load_multisig_tx(blob, txs, NULL);
4017 er.
message =
"Failed to parse multisig tx data.";
4021 std::vector<crypto::hash> txids;
4024 bool r = m_wallet->sign_multisig_tx(txs, txids);
4028 er.
message =
"Failed to sign multisig tx";
4032 catch (
const std::exception &e)
4051 if (!m_wallet)
return not_open(er);
4055 er.
message =
"Command unavailable in restricted mode.";
4060 if (!m_wallet->multisig(&ready, &
threshold, &total))
4063 er.
message =
"This wallet is not multisig";
4069 er.
message =
"This wallet is multisig, but not yet finalized";
4077 er.
message =
"Failed to parse hex.";
4082 bool r = m_wallet->load_multisig_tx(blob, txs, NULL);
4086 er.
message =
"Failed to parse multisig tx data.";
4093 er.
message =
"Not enough signers signed this transaction.";
4099 for (
auto &ptx: txs.
m_ptx)
4101 m_wallet->commit_tx(ptx);
4105 catch (
const std::exception &e)
4123 if (!req.any_net_type && !m_wallet)
return not_open(er);
4124 for (
const auto &net_type: net_types)
4126 if (!req.any_net_type && (!m_wallet || net_type.type != m_wallet->nettype()))
4128 if (req.allow_openalias)
4132 [&er, &
address](
const std::string &url,
const std::vector<std::string> &addresses,
bool dnssec_valid)->std::string {
4135 er.message = std::string(
"Invalid DNSSEC for ") + url;
4138 if (addresses.empty())
4140 er.message = std::string(
"No ETN address found at ") + url;
4155 res.integrated =
info.has_payment_id;
4156 res.subaddress =
info.is_subaddress;
4157 res.nettype = net_type.stype;
4169 if (!m_wallet)
return not_open(er);
4173 er.
message =
"Command unavailable in restricted mode.";
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)
4181 ssl_allowed_fingerprints.push_back({});
4182 std::vector<uint8_t> &v = ssl_allowed_fingerprints.back();
4188 if (req.ssl_allow_any_cert)
4190 else if (!ssl_allowed_fingerprints.empty() || !req.ssl_ca_file.empty())
4204 const bool verification_required =
4211 er.
message =
"SSL is enabled but no user certificate or fingerprints were provided";
4215 if (!m_wallet->set_daemon(req.address, boost::none, req.trusted,
std::move(ssl_options)))
4229 er.
message =
"Command unavailable in restricted mode.";
4233 if (req.level < 0 || req.level > 4)
4236 er.
message =
"Error: log level not valid";
4248 er.
message =
"Command unavailable in restricted mode.";
4268 const boost::program_options::variables_map& vm;
4270 std::unique_ptr<tools::wallet_rpc_server> wrpc;
4273 t_daemon(boost::program_options::variables_map
const & _vm)
4275 , wrpc(new
tools::wallet_rpc_server)
4281 std::unique_ptr<tools::wallet2> wal;
4286 if (testnet && stagenet)
4299 const auto password_prompt = prompt_for_password ? password_prompter :
nullptr;
4301 if(!wallet_file.empty() && !from_json.empty())
4307 if (!wallet_dir.empty())
4313 if (wallet_file.empty() && from_json.empty())
4320 if(!wallet_file.empty())
4331 catch (
const std::exception &e)
4333 MERROR(
"Error creating wallet: " << e.what());
4349 wal->refresh(wal->is_trusted_daemon());
4360 catch (
const std::exception& e)
4366 if (wal) wrpc->set_wallet(wal.release());
4367 bool r = wrpc->init(&vm);
4370 wrpc->send_stop_signal();
4377 catch (
const std::exception &e)
4389 catch (
const std::exception& e)
4399 wrpc->send_stop_signal();
4436 namespace po = boost::program_options;
4441 po::options_description hidden_options(
"Hidden");
4443 po::options_description desc_params(
wallet_args::tr(
"Wallet options"));
4455 desc_params.add(hidden_options);
4457 boost::optional<po::variables_map> vm;
4458 bool should_terminate =
false;
4461 "electroneum-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
4464 po::positional_options_description(),
4466 "electroneum-wallet-rpc.log",
4473 if (should_terminate)
std::string get_public_address_str(network_type nettype) const
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
bool has_strong_verification(boost::string_ref host) const noexcept
\retrurn True if host can be verified using this configuration WITHOUT system "root" CAs.
ssl_authentication_t auth
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)
std::string const & name() const
t_daemon create_daemon(boost::program_options::variables_map const &vm)
#define CORE_RPC_STATUS_OK
#define CRYPTONOTE_MAX_BLOCK_NUMBER
Mnemonic seed generation and wallet restoration from them.
void * memcpy(void *a, const void *b, size_t c)
const char * i18n_translate(const char *s, const std::string &context)
std::string mlog_get_categories()
void mlog_set_log(const char *log)
void mlog_set_log_level(int level)
#define CATCH_ENTRY_L0(lacation, return_val)
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
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)
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
std::vector< const Language::Base * > get_language_list()
bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string &dst, size_t len, bool duplicate, std::string &language_name)
Converts seed words to bytes (secret key).
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
const std::string old_language_name
bool bytes_to_words(const char *src, size_t len, epee::wipeable_string &words, const std::string &language_name)
Converts bytes (secret key) to seed words.
void rand(size_t N, uint8_t *bytes)
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 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)
@ none
Do not verify peer.
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len)
void set_console_color(int color, bool bright)
T & unwrap(mlocked< T > &src)
void reset_console_color()
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
error
Tracks LMDB error codes.
const T & move(const T &t)
boost::filesystem::path data_dir
const char * tr(const char *str)
std::pair< boost::optional< boost::program_options::variables_map >, bool > main(int argc, char **argv, const char *const usage, const char *const notice, boost::program_options::options_description desc_params, const boost::program_options::positional_options_description &positional_options, const std::function< void(const std::string &, bool)> &print, const char *default_log_name, bool log_to_console)
command_line::arg_descriptor< std::string > arg_generate_from_json()
command_line::arg_descriptor< std::string > arg_wallet_file()
CXA_THROW_INFO_T void(* dest)(void *))
unsigned __int64 uint64_t
static boost::optional< rpc_args > process(const boost::program_options::variables_map &vm, const bool any_cert_option=false)
static void init_options(boost::program_options::options_description &desc, const bool any_cert_option=false)
account_public_address addr
static epee::wipeable_string wipeable_string(const span< const std::uint8_t > src)
static std::string string(const span< const std::uint8_t > src)
bool nonexistent_utxo_seen
cryptonote::subaddress_index subaddr_index
uint64_t suggested_confirmations_threshold
std::list< transfer_destination > destinations
std::vector< cryptonote::subaddress_index > subaddr_indices
#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