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 // seconds 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" || entry.
type ==
"sc-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 {
773 if (addresses.empty())
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())
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));
918 if (multisig_txset.empty())
921 er.
message =
"Failed to save multisig tx set after creation";
929 if (unsigned_txset.empty())
932 er.
message =
"Failed to save unsigned tx set after creation";
936 else if (!do_not_relay)
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))
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))
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.";
1060 er.
message =
"command not supported by HW wallet";
1066 er.
message =
"command not supported by watch-only wallet";
1074 er.
message =
"Failed to parse hex.";
1082 er.
message =
"cannot load unsigned_txset";
1086 std::vector<tools::wallet2::pending_tx> ptxs;
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.";
1141 er.
message =
"command not supported by HW wallet";
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.";
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.";
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.";
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;
1358 er.
message =
"Failed to parse signed tx data.";
1362 catch (
const std::exception &e)
1371 for (
auto &ptx: ptx_vector)
1377 catch (
const std::exception &e)
1389 if (!m_wallet)
return not_open(er);
1393 er.
message =
"Command unavailable in restricted mode.";
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)
1446 subaddr_indices.insert(i);
1450 subaddr_indices= req.subaddr_indices;
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";
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.";
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())
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;
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;
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;
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;
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)
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;
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)
1911 er.
message =
"This wallet is multisig, but not yet finalized";
1917 er.
message =
"Failed to get multisig seed.";
1926 er.
message =
"The wallet is watch-only. Cannot retrieve seed.";
1932 er.
message =
"The wallet is non-deterministic. Cannot display 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)
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.";
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 {
2024 if (addresses.empty())
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())
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())
2137 if (!m_wallet)
return not_open(er);
2141 er.
message =
"Command unavailable in restricted mode.";
2152 if (!m_wallet)
return not_open(er);
2156 er.
message =
"Command unavailable in restricted mode.";
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";
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";
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";
2311 catch (
const std::exception &e)
2322 if (!m_wallet)
return not_open(er);
2328 er.
message =
"TX ID has invalid format";
2336 catch (
const std::exception &e)
2347 if (!m_wallet)
return not_open(er);
2353 er.
message =
"TX ID has invalid format";
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;
2380 er.
message =
"Account index is out of bound";
2383 account_minreserve = std::make_pair(req.account_index, req.amount);
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";
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;
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.sc_migration)
2487 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
2489 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2490 res.sc_migration.push_back(wallet_rpc::transfer_entry());
2491 fill_transfer_entry(
res.sc_migration.back(), i->first, i->second);
2495 if (req.pending || req.failed) {
2496 std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
2498 for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
2501 if (!((req.failed && is_failed) || (!is_failed && req.pending)))
2503 std::list<wallet_rpc::transfer_entry> &entries = is_failed ?
res.failed :
res.pending;
2504 entries.push_back(wallet_rpc::transfer_entry());
2505 fill_transfer_entry(entries.back(), i->first, i->second);
2513 std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
2515 for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2516 res.pool.push_back(wallet_rpc::transfer_entry());
2517 fill_transfer_entry(
res.pool.back(), i->first, i->second);
2526 if (!m_wallet)
return not_open(er);
2530 er.
message =
"Command unavailable in restricted mode.";
2539 er.
message =
"Transaction ID has invalid format";
2543 if(
sizeof(txid) == txid_blob.size())
2545 txid = *
reinterpret_cast<const crypto::hash*
>(txid_blob.data());
2550 er.
message =
"Transaction ID has invalid size: " + req.txid;
2557 er.
message =
"Account index is out of bound";
2561 std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
2563 for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
2564 if (i->second.m_tx_hash == txid)
2566 res.transfers.resize(
res.transfers.size() + 1);
2567 fill_transfer_entry(
res.transfers.back(), i->second.m_tx_hash, i->first, i->second);
2571 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out;
2573 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
2574 if (i->first == txid)
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::confirmed_transfer_details>> migrations;
2583 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = migrations.begin(); i != migrations.end(); ++i) {
2584 if (i->first == txid)
2586 res.transfers.resize(
res.transfers.size() + 1);
2587 fill_transfer_entry(
res.transfers.back(), i->first, i->second);
2591 std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> sc_migrations;
2593 for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = sc_migrations.begin(); i != sc_migrations.end(); ++i) {
2594 if (i->first == txid)
2596 res.transfers.resize(
res.transfers.size() + 1);
2597 fill_transfer_entry(
res.transfers.back(), i->first, i->second);
2601 std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
2603 for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
2604 if (i->first == txid)
2606 res.transfers.resize(
res.transfers.size() + 1);
2607 fill_transfer_entry(
res.transfers.back(), i->first, i->second);
2613 std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
2615 for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
2616 if (i->second.m_pd.m_tx_hash == txid)
2618 res.transfers.resize(
res.transfers.size() + 1);
2619 fill_transfer_entry(
res.transfers.back(), i->first, i->second);
2623 if (!
res.transfers.empty())
2625 res.transfer =
res.transfers.front();
2630 er.
message =
"Transaction not found.";
2636 if (!m_wallet)
return not_open(er);
2640 er.
message =
"Command unavailable in restricted mode.";
2646 er.
message =
"command not supported by HW wallet";
2654 catch (
const std::exception &e)
2665 if (!m_wallet)
return not_open(er);
2669 er.
message =
"Command unavailable in restricted mode.";
2675 er.
message =
"command not supported by HW wallet";
2683 er.
message =
"Failed to parse hex.";
2691 catch (
const std::exception &e)
2702 if (!m_wallet)
return not_open(er);
2705 std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->
export_key_images(req.all);
2706 res.offset = ski.first;
2707 res.signed_key_images.resize(ski.second.size());
2708 for (
size_t n = 0; n < ski.second.size(); ++n)
2715 catch (
const std::exception& e)
2726 if (!m_wallet)
return not_open(er);
2730 er.
message =
"Command unavailable in restricted mode.";
2736 er.
message =
"This command requires a trusted daemon.";
2741 std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
2742 ski.resize(req.signed_key_images.size());
2743 for (
size_t n = 0; n < ski.size(); ++n)
2748 er.
message =
"failed to parse key image";
2755 er.
message =
"failed to parse signature";
2762 res.unspent = unspent;
2766 catch (
const std::exception& e)
2777 if (!m_wallet)
return not_open(er);
2779 std::string uri = m_wallet->
make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name,
error);
2793 if (!m_wallet)
return not_open(er);
2795 if (!m_wallet->
parse_uri(req.uri,
res.uri.address,
res.uri.payment_id,
res.uri.amount,
res.uri.tx_description,
res.uri.recipient_name,
res.unknown_parameters,
error))
2806 if (!m_wallet)
return not_open(er);
2808 if (req.entries.empty())
2811 for (
const auto &entry: ab)
2818 if (idx >= ab.size())
2824 const auto &entry = ab[idx];
2833 if (!m_wallet)
return not_open(er);
2837 er.
message =
"Command unavailable in restricted mode.";
2845 [&er](
const std::string &url,
const std::vector<std::string> &addresses,
bool dnssec_valid)->
std::string {
2851 if (addresses.empty())
2856 return addresses[0];
2864 if (
info.has_payment_id)
2866 memcpy(payment_id.data,
info.payment_id.data, 8);
2867 memset(payment_id.data + 8, 0, 24);
2869 if (!req.payment_id.empty())
2871 if (
info.has_payment_id)
2874 er.
message =
"Separate payment ID given with integrated address";
2885 er.
message =
"Payment id has invalid format: \"" + req.payment_id +
"\", expected 64 character string";
2891 er.
message =
"Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address";
2899 er.
message =
"Failed to add address book entry";
2908 if (!m_wallet)
return not_open(er);
2912 er.
message =
"Command unavailable in restricted mode.";
2917 if (req.index >= ab.size())
2926 er.
message =
"Failed to delete address book entry";
2934 if (!m_wallet)
return not_open(er);
2938 er.
message =
"Command unavailable in restricted mode.";
2946 catch (
const std::exception& e)
2959 er.
message =
"Command unavailable in restricted mode.";
2968 catch (
const std::exception& e)
2978 if (!m_wallet)
return not_open(er);
2982 er.
message =
"Command unavailable in restricted mode.";
2990 catch (
const std::exception& e)
3000 if (!m_wallet)
return not_open(er);
3004 er.
message =
"This command requires a trusted daemon.";
3009 if (req.threads_count < 1 || max_mining_threads_count < req.threads_count)
3012 er.
message =
"The specified number of threads is inappropriate.";
3018 daemon_req.threads_count = req.threads_count;
3019 daemon_req.do_background_mining = req.do_background_mining;
3020 daemon_req.ignore_battery = req.ignore_battery;
3023 bool r = m_wallet->
invoke_http_json(
"/start_mining", daemon_req, daemon_res);
3027 er.
message =
"Couldn't start mining due to unknown error.";
3035 if (!m_wallet)
return not_open(er);
3038 bool r = m_wallet->
invoke_http_json(
"/stop_mining", daemon_req, daemon_res);
3042 er.
message =
"Couldn't stop mining due to unknown error.";
3057 if (m_wallet_dir.empty())
3060 er.
message =
"No wallet dir configured";
3064 namespace po = boost::program_options;
3065 po::variables_map vm2;
3066 const char *ptr = strchr(req.filename.c_str(),
'/');
3069 ptr = strchr(req.filename.c_str(),
'\\');
3071 ptr = strchr(req.filename.c_str(),
':');
3076 er.
message =
"Invalid filename";
3079 std::string wallet_file = req.filename.empty() ?
"" : (m_wallet_dir +
"/" + req.filename);
3081 std::vector<std::string> languages;
3083 std::vector<std::string>::iterator it;
3085 it = std::find(languages.begin(), languages.end(), req.language);
3086 if (it == languages.end())
3089 it = std::find(languages.begin(), languages.end(), req.language);
3091 if (it == languages.end())
3094 er.
message =
"Unknown language: " + req.language;
3099 po::options_description desc(
"dummy");
3101 const char *argv[4];
3103 argv[0] =
"wallet-rpc";
3104 argv[1] =
"--password";
3105 argv[2] = req.password.c_str();
3115 er.
message =
"Failed to create wallet";
3127 wal->
generate(wallet_file, req.password, dummy_key,
false,
false);
3129 catch (
const std::exception& e)
3137 er.
message =
"Failed to generate wallet";
3147 catch (
const std::exception& e)
3154 m_wallet = wal.release();
3160 if (m_wallet_dir.empty())
3163 er.
message =
"No wallet dir configured";
3167 namespace po = boost::program_options;
3168 po::variables_map vm2;
3169 const char *ptr = strchr(req.filename.c_str(),
'/');
3172 ptr = strchr(req.filename.c_str(),
'\\');
3174 ptr = strchr(req.filename.c_str(),
':');
3179 er.
message =
"Invalid filename";
3182 if (m_wallet && req.autosave_current)
3188 catch (
const std::exception& e)
3194 std::string wallet_file = m_wallet_dir +
"/" + req.filename;
3196 po::options_description desc(
"dummy");
3198 const char *argv[4];
3200 argv[0] =
"wallet-rpc";
3201 argv[1] =
"--password";
3202 argv[2] = req.password.c_str();
3208 std::unique_ptr<tools::wallet2> wal =
nullptr;
3212 catch (
const std::exception& e)
3219 er.
message =
"Failed to open wallet";
3225 m_wallet = wal.release();
3231 if (!m_wallet)
return not_open(er);
3233 if (req.autosave_current)
3239 catch (
const std::exception& e)
3252 if (!m_wallet)
return not_open(er);
3256 er.
message =
"Command unavailable in restricted mode.";
3266 catch (
const std::exception& e)
3275 er.
message =
"Invalid original password.";
3281 void wallet_rpc_server::handle_rpc_exception(
const std::exception_ptr& e,
epee::json_rpc::error& er,
int default_error_code) {
3284 std::rethrow_exception(e);
3314 er.
message = (boost::format(
tr(
"Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee)")) %
3329 er.
message =
"Cannot create wallet. Already exists.";
3331 catch (
const error::invalid_password& e)
3334 er.
message =
"Invalid password.";
3336 catch (
const error::account_index_outofbound& e)
3341 catch (
const error::address_index_outofbound& e)
3346 catch (
const error::signature_check_failed& e)
3351 catch (
const std::exception& e)
3353 er.
code = default_error_code;
3359 er.
message =
"WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
3365 if (m_wallet_dir.empty())
3368 er.
message =
"No wallet dir configured";
3373 if (req.viewkey.empty())
3376 er.
message =
"field 'viewkey' is mandatory. Please provide a view key you want to restore from.";
3379 if (req.address.empty())
3382 er.
message =
"field 'address' is mandatory. Please provide a public address.";
3386 namespace po = boost::program_options;
3387 po::variables_map vm2;
3388 const char *ptr = strchr(req.filename.c_str(),
'/');
3391 ptr = strchr(req.filename.c_str(),
'\\');
3393 ptr = strchr(req.filename.c_str(),
':');
3398 er.
message =
"Invalid filename";
3401 std::string wallet_file = req.filename.empty() ?
"" : (m_wallet_dir +
"/" + req.filename);
3403 if (!wallet_file.empty())
3407 boost::system::error_code ignored_ec;
3410 catch (
const std::exception &e)
3413 er.
message =
"Wallet already exists.";
3419 po::options_description desc(
"dummy");
3421 const char *argv[4];
3423 argv[0] =
"wallet-rpc";
3424 argv[1] =
"--password";
3425 argv[2] = req.password.c_str();
3433 std::unique_ptr<wallet2> wal;
3438 er.
message =
"Failed to create wallet";
3446 er.
message =
"Failed to parse public address";
3456 er.
message =
"Failed to parse view key secret key";
3460 if (m_wallet && req.autosave_current)
3464 if (!wallet_file.empty())
3467 catch (
const std::exception &e)
3476 if (!req.spendkey.empty())
3483 er.
message =
"Failed to parse spend key secret key";
3486 wal->generate(wallet_file,
std::move(rc.second).password(),
info.address,
spendkey, viewkey,
false);
3487 res.info =
"Wallet has been generated successfully.";
3491 wal->generate(wallet_file,
std::move(rc.second).password(),
info.address, viewkey,
false);
3492 res.info =
"Watch-only wallet has been generated successfully.";
3494 MINFO(
"Wallet has been generated.\n");
3496 catch (
const std::exception &e)
3505 er.
message =
"Failed to generate wallet";
3512 wal->set_refresh_from_block_height(req.restore_height);
3513 wal->rewrite(wallet_file, password);
3515 catch (
const std::exception &e)
3523 m_wallet = wal.release();
3530 if (m_wallet_dir.empty())
3533 er.
message =
"No wallet dir configured";
3538 if (req.seed.empty())
3541 er.
message =
"field 'seed' is mandatory. Please provide a seed you want to restore from.";
3545 namespace po = boost::program_options;
3546 po::variables_map vm2;
3547 const char *ptr = strchr(req.filename.c_str(),
'/');
3550 ptr = strchr(req.filename.c_str(),
'\\');
3552 ptr = strchr(req.filename.c_str(),
':');
3557 er.
message =
"Invalid filename";
3560 std::string wallet_file = req.filename.empty() ?
"" : (m_wallet_dir +
"/" + req.filename);
3562 if (!wallet_file.empty())
3566 boost::system::error_code ignored_ec;
3569 catch (
const std::exception &e)
3572 er.
message =
"Wallet already exists.";
3584 er.
message =
"Electrum-style word list failed verification";
3588 if (m_wallet && req.autosave_current)
3594 catch (
const std::exception &e)
3603 if (!req.seed_offset.empty())
3609 po::options_description desc(
"dummy");
3611 const char *argv[4];
3613 argv[0] =
"wallet-rpc";
3614 argv[1] =
"--password";
3615 argv[2] = req.password.c_str();
3623 std::unique_ptr<wallet2> wal;
3628 er.
message =
"Failed to create wallet";
3638 if (was_deprecated_wallet)
3641 res.was_deprecated =
true;
3646 if (req.language.empty())
3649 er.
message =
"Wallet was using the old seed language. You need to specify a new seed language.";
3652 std::vector<std::string> language_list;
3653 std::vector<std::string> language_list_en;
3656 if (std::find(language_list.begin(), language_list.end(), req.language) == language_list.end() &&
3657 std::find(language_list_en.begin(), language_list_en.end(), req.language) == language_list_en.end())
3660 er.
message =
"Wallet was using the old seed language, and the specified new seed language is invalid.";
3663 mnemonic_language = req.language;
3666 wal->set_seed_language(mnemonic_language);
3671 recovery_val = wal->generate(wallet_file,
std::move(rc.second).password(), recovery_key,
true,
false,
false);
3672 MINFO(
"Wallet has been restored.\n");
3674 catch (
const std::exception &e)
3685 er.
message =
"Failed to encode seed";
3693 er.
message =
"Failed to generate wallet";
3700 wal->set_refresh_from_block_height(req.restore_height);
3701 wal->rewrite(wallet_file, password);
3703 catch (
const std::exception &e)
3711 m_wallet = wal.release();
3713 res.info =
"Wallet has been restored successfully.";
3719 if (!m_wallet)
return not_open(er);
3726 if (!m_wallet)
return not_open(er);
3730 er.
message =
"Command unavailable in restricted mode.";
3736 er.
message =
"This wallet is already multisig";
3742 er.
message =
"wallet is watch-only and cannot be made multisig";
3752 if (!m_wallet)
return not_open(er);
3756 er.
message =
"Command unavailable in restricted mode.";
3762 er.
message =
"This wallet is already multisig";
3768 er.
message =
"wallet is watch-only and cannot be made multisig";
3774 res.multisig_info = m_wallet->
make_multisig(req.password, req.multisig_info, req.threshold);
3777 catch (
const std::exception &e)
3789 if (!m_wallet)
return not_open(er);
3793 er.
message =
"Command unavailable in restricted mode.";
3800 er.
message =
"This wallet is not multisig";
3806 er.
message =
"This wallet is multisig, but not yet finalized";
3815 catch (
const std::exception &e)
3829 if (!m_wallet)
return not_open(er);
3833 er.
message =
"Command unavailable in restricted mode.";
3841 er.
message =
"This wallet is not multisig";
3847 er.
message =
"This wallet is multisig, but not yet finalized";
3854 er.
message =
"Needs multisig export info from more participants";
3858 std::vector<cryptonote::blobdata>
info;
3859 info.resize(req.info.size());
3860 for (
size_t n = 0; n <
info.size(); ++n)
3865 er.
message =
"Failed to parse hex.";
3874 catch (
const std::exception &e)
3877 er.
message =
"Error calling import_multisig";
3887 catch (
const std::exception &e)
3889 er.
message =
std::string(
"Success, but failed to update spent status after import multisig info: ") + e.what();
3894 er.
message =
"Success, but cannot update spent status after import multisig info as daemon is untrusted";
3902 if (!m_wallet)
return not_open(er);
3906 er.
message =
"Command unavailable in restricted mode.";
3914 er.
message =
"This wallet is not multisig";
3920 er.
message =
"This wallet is multisig, and already finalized";
3924 if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
3927 er.
message =
"Needs multisig info from more participants";
3936 er.
message =
"Error calling finalize_multisig";
3940 catch (
const std::exception &e)
3953 if (!m_wallet)
return not_open(er);
3957 er.
message =
"Command unavailable in restricted mode.";
3965 er.
message =
"This wallet is not multisig";
3972 er.
message =
"This wallet is multisig, and already finalized";
3976 if (req.multisig_info.size() < 1 || req.multisig_info.size() > total)
3979 er.
message =
"Needs multisig info from more participants";
3986 if (
res.multisig_info.empty())
3991 catch (
const std::exception &e)
4002 if (!m_wallet)
return not_open(er);
4006 er.
message =
"Command unavailable in restricted mode.";
4014 er.
message =
"This wallet is not multisig";
4020 er.
message =
"This wallet is multisig, but not yet finalized";
4028 er.
message =
"Failed to parse hex.";
4037 er.
message =
"Failed to parse multisig tx data.";
4041 std::vector<crypto::hash> txids;
4048 er.
message =
"Failed to sign multisig tx";
4052 catch (
const std::exception &e)
4071 if (!m_wallet)
return not_open(er);
4075 er.
message =
"Command unavailable in restricted mode.";
4083 er.
message =
"This wallet is not multisig";
4089 er.
message =
"This wallet is multisig, but not yet finalized";
4097 er.
message =
"Failed to parse hex.";
4106 er.
message =
"Failed to parse multisig tx data.";
4113 er.
message =
"Not enough signers signed this transaction.";
4119 for (
auto &ptx: txs.
m_ptx)
4125 catch (
const std::exception &e)
4143 if (!req.any_net_type && !m_wallet)
return not_open(er);
4144 for (
const auto &net_type: net_types)
4146 if (!req.any_net_type && (!m_wallet || net_type.type != m_wallet->
nettype()))
4148 if (req.allow_openalias)
4152 [&er, &
address](
const std::string &url,
const std::vector<std::string> &addresses,
bool dnssec_valid)->std::string {
4155 er.message = std::string(
"Invalid DNSSEC for ") + url;
4158 if (addresses.empty())
4175 res.integrated =
info.has_payment_id;
4176 res.subaddress =
info.is_subaddress;
4177 res.nettype = net_type.stype;
4189 if (!m_wallet)
return not_open(er);
4193 er.
message =
"Command unavailable in restricted mode.";
4197 std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints;
4198 ssl_allowed_fingerprints.reserve(req.ssl_allowed_fingerprints.size());
4199 for (
const std::string &fp: req.ssl_allowed_fingerprints)
4201 ssl_allowed_fingerprints.push_back({});
4202 std::vector<uint8_t> &v = ssl_allowed_fingerprints.back();
4208 if (req.ssl_allow_any_cert)
4210 else if (!ssl_allowed_fingerprints.empty() || !req.ssl_ca_file.empty())
4224 const bool verification_required =
4231 er.
message =
"SSL is enabled but no user certificate or fingerprints were provided";
4235 if (!m_wallet->set_daemon(req.address, boost::none, req.trusted,
std::move(ssl_options)))
4249 er.
message =
"Command unavailable in restricted mode.";
4253 if (req.level < 0 || req.level > 4)
4256 er.
message =
"Error: log level not valid";
4268 er.
message =
"Command unavailable in restricted mode.";
4288 const boost::program_options::variables_map& vm;
4290 std::unique_ptr<tools::wallet_rpc_server> wrpc;
4293 t_daemon(boost::program_options::variables_map
const & _vm)
4295 , wrpc(new
tools::wallet_rpc_server)
4301 std::unique_ptr<tools::wallet2> wal;
4306 if (testnet && stagenet)
4319 const auto password_prompt = prompt_for_password ? password_prompter :
nullptr;
4321 if(!wallet_file.empty() && !from_json.empty())
4327 if (!wallet_dir.empty())
4333 if (wallet_file.empty() && from_json.empty())
4340 if(!wallet_file.empty())
4351 catch (
const std::exception &e)
4353 MERROR(
"Error creating wallet: " << e.what());
4380 catch (
const std::exception& e)
4387 bool r = wrpc->
init(&vm);
4397 catch (
const std::exception &e)
4409 catch (
const std::exception& e)
4456 namespace po = boost::program_options;
4461 po::options_description hidden_options(
"Hidden");
4463 po::options_description desc_params(
wallet_args::tr(
"Wallet options"));
4475 desc_params.add(hidden_options);
4477 boost::optional<po::variables_map> vm;
4478 bool should_terminate =
false;
4481 "electroneum-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
4484 po::positional_options_description(),
4486 "electroneum-wallet-rpc.log",
4493 if (should_terminate)
std::string base64_encode(unsigned char const *bytes_to_encode, size_t in_len)
#define WALLET_RPC_ERROR_CODE_NOT_MULTISIG
bool parse_tx_extra(const std::vector< uint8_t > &tx_extra, std::vector< tx_extra_field > &tx_extra_fields)
int main(int argc, char **argv)
std::list< transfer_destination > destinations
net_utils::boosted_tcp_server< net_utils::http::http_custom_handler< epee::net_utils::connection_context_base > > m_net_server
#define WALLET_RPC_ERROR_CODE_NOT_OPEN
#define MKDIR(path, mode)
#define WALLET_RPC_ERROR_CODE_WRONG_URI
std::string get_account_address_as_str(network_type nettype, bool subaddress, account_public_address const &adr)
command_line::arg_descriptor< std::string > arg_wallet_file()
void init_options(boost::program_options::options_description &hidden_options, boost::program_options::options_description &normal_options)
size_t size() const noexcept
std::vector< const Language::Base * > get_language_list()
#define WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR
bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s)
#define WALLET_RPC_ERROR_CODE_MULTISIG_SUBMISSION
#define WALLET_RPC_ERROR_CODE_NO_WALLET_DIR
CXA_THROW_INFO_T void(* dest)(void *))
epee::misc_utils::struct_init< response_t > response
std::string print_etn(uint64_t amount, unsigned int decimal_point)
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define CRYPTONOTE_MAX_BLOCK_NUMBER
const std::string old_language_name
void mlog_set_log(const char *log)
std::vector< cryptonote::subaddress_index > subaddr_indices
static boost::optional< rpc_args > process(const boost::program_options::variables_map &vm, const bool any_cert_option=false)
#define WALLET_RPC_ERROR_CODE_INVALID_PASSWORD
#define WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE
bool find_tx_extra_field_by_type(const std::vector< tx_extra_field > &tx_extra_fields, T &field, size_t index=0)
cryptonote::subaddress_index subaddr_index
const char * tr(const char *str)
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).
std::shared_ptr< messages::Electroneum::ElectroneumGetTxKeyRequest > get_tx_key(const hw::device_cold::tx_key_data_t &tx_data)
std::string get_public_address_str(network_type nettype) const
boost::filesystem::path data_dir
#define WALLET_RPC_ERROR_CODE_WRONG_ADDRESS
#define DEFAULT_AUTO_REFRESH_PERIOD
const char * i18n_translate(const char *s, const std::string &context)
#define WALLET_RPC_ERROR_CODE_INVALID_LOG_LEVEL
t_daemon(boost::program_options::variables_map const &_vm)
static void init_options(boost::program_options::options_description &desc, const bool any_cert_option=false)
static epee::wipeable_string wipeable_string(const span< const std::uint8_t > src)
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)
bool hex_to_pod(T &pod) const
#define CORE_RPC_STATUS_OK
#define WALLET_RPC_VERSION
const account_keys & get_keys() const
#define WALLET_RPC_ERROR_CODE_MULTISIG_SIGNATURE
#define WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY
void reset_console_color()
blobdata tx_to_blob(const transaction &tx)
void rand(size_t N, uint8_t *bytes)
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.
uint64_t suggested_confirmations_threshold
std::string get_account_integrated_address_as_str(network_type nettype, account_public_address const &adr, crypto::hash8 const &payment_id)
std::string mlog_get_categories()
#define WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS
#define WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA
ssl_authentication_t auth
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)
#define WALLET_RPC_ERROR_CODE_BAD_HEX
unsigned __int64 uint64_t
bool daemonize(int argc, char const *argv[], T_executor &&executor, boost::program_options::variables_map const &vm)
const command_line::arg_descriptor< std::string, false, true, 2 > arg_data_dir
#define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED
command_line::arg_descriptor< std::string > arg_generate_from_json()
bool get_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash &payment_id)
bool run_interactive(boost::program_options::variables_map const &vm)
#define WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA
#define WALLET_RPC_ERROR_CODE_WATCH_ONLY
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_OUTS_TO_MIX
bool get_encrypted_payment_id_from_tx_extra_nonce(const blobdata &extra_nonce, crypto::hash8 &payment_id)
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
#define WALLET_RPC_ERROR_CODE_WRONG_INDEX
#define WALLET_RPC_ERROR_CODE_ZERO_DESTINATION
#define WALLET_RPC_ERROR_CODE_DENIED
crypto::secret_key m_view_secret_key
bool run(size_t threads_count, bool wait=true)
void set_console_color(int color, bool bright)
account_public_address addr
#define WALLET_RPC_ERROR_CODE_BAD_TX_METADATA
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)
const T & move(const T &t)
t_daemon create_daemon(boost::program_options::variables_map const &vm)
#define WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
void * memcpy(void *a, const void *b, size_t c)
crypto::secret_key m_spend_secret_key
crypto::hash get_transaction_hash(const transaction &t)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
bool add_extra_nonce_to_tx_extra(std::vector< uint8_t > &tx_extra, const blobdata &extra_nonce)
boost::program_options::basic_parsed_options< charT > parse_command_line(int argc, const charT *const argv[], const boost::program_options::options_description &desc, bool allow_unregistered=false)
bool get_account_address_from_str(address_parse_info &info, network_type nettype, std::string const &str)
ssl_verification_t verification
#define WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUT_OF_BOUNDS
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_ETN
Mnemonic seed generation and wallet restoration from them.
T & unwrap(mlocked< T > &src)
bool nonexistent_utxo_seen
static std::string string(const span< const std::uint8_t > src)
bool has_strong_verification(boost::string_ref host) const noexcept
True if host can be verified using this configuration WITHOUT system "root" CAs.
#define WALLET_RPC_ERROR_CODE_TX_TOO_LARGE
#define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR
crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase)
#define WALLET_RPC_ERROR_CODE_TRANSFER_TYPE
#define WALLET_RPC_ERROR_CODE_NO_TXKEY
#define WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_UNLOCKED_ETN
std::string to_string(t_connection_type type)
#define WALLET_RPC_ERROR_CODE_WRONG_TXID
#define WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION
void set_payment_id_to_tx_extra_nonce(blobdata &extra_nonce, const crypto::hash &payment_id)
#define WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
#define WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE
bool run_non_interactive(boost::program_options::variables_map const &vm)
const char * data() const noexcept
#define CATCH_ENTRY_L0(lacation, return_val)
error
Tracks LMDB error codes.
void mlog_set_log_level(int level)
#define WALLET_RPC_ERROR_CODE_WRONG_KEY
#define WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID
static std::string const NAME
#define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
std::string const & name() const
#define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC
#define WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS
#define WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG