diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/CMakeLists.txt | 65 | ||||
-rw-r--r-- | src/wallet/api/address_book.cpp | 50 | ||||
-rw-r--r-- | src/wallet/node_rpc_proxy.cpp | 4 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 107 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 48 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 157 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.h | 4 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server_commands_defs.h | 72 |
8 files changed, 315 insertions, 192 deletions
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 3be1a6f6b..a0a166a93 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -77,42 +77,43 @@ target_link_libraries(wallet PRIVATE ${EXTRA_LIBRARIES}) -set(wallet_rpc_sources - wallet_rpc_server.cpp) +if(NOT IOS) + set(wallet_rpc_sources + wallet_rpc_server.cpp) -set(wallet_rpc_headers) + set(wallet_rpc_headers) -set(wallet_rpc_private_headers - wallet_rpc_server.h) + set(wallet_rpc_private_headers + wallet_rpc_server.h) -monero_private_headers(wallet_rpc_server - ${wallet_rpc_private_headers}) -monero_add_executable(wallet_rpc_server - ${wallet_rpc_sources} - ${wallet_rpc_headers} - ${wallet_rpc_private_headers}) - -target_link_libraries(wallet_rpc_server - PRIVATE - wallet - rpc_base - cryptonote_core - cncrypto - common - version - daemonizer - ${EPEE_READLINE} - ${Boost_CHRONO_LIBRARY} - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${Boost_THREAD_LIBRARY} - ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) -set_property(TARGET wallet_rpc_server - PROPERTY - OUTPUT_NAME "monero-wallet-rpc") -install(TARGETS wallet_rpc_server DESTINATION bin) + monero_private_headers(wallet_rpc_server + ${wallet_rpc_private_headers}) + monero_add_executable(wallet_rpc_server + ${wallet_rpc_sources} + ${wallet_rpc_headers} + ${wallet_rpc_private_headers}) + target_link_libraries(wallet_rpc_server + PRIVATE + wallet + rpc_base + cryptonote_core + cncrypto + common + version + daemonizer + ${EPEE_READLINE} + ${Boost_CHRONO_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) + set_property(TARGET wallet_rpc_server + PROPERTY + OUTPUT_NAME "monero-wallet-rpc") + install(TARGETS wallet_rpc_server DESTINATION bin) +endif() # build and install libwallet_merged only if we building for GUI if (BUILD_GUI_DEPS) diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index 7be78bba7..005ddf7ee 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -55,37 +55,14 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa return false; } - crypto::hash payment_id = crypto::null_hash; - bool has_long_pid = (payment_id_str.empty())? false : tools::wallet2::parse_long_payment_id(payment_id_str, payment_id); - - // Short payment id provided - if(payment_id_str.length() == 16) { - m_errorString = tr("Invalid payment ID. Short payment ID should only be used in an integrated address"); - m_errorCode = Invalid_Payment_Id; - return false; - } - - // long payment id provided but not valid - if(!payment_id_str.empty() && !has_long_pid) { - m_errorString = tr("Invalid payment ID"); - m_errorCode = Invalid_Payment_Id; - return false; - } - - // integrated + long payment id provided - if(has_long_pid && info.has_payment_id) { - m_errorString = tr("Integrated address and long payment ID can't be used at the same time"); + if (!payment_id_str.empty()) + { + m_errorString = tr("Payment ID supplied: this is obsolete"); m_errorCode = Invalid_Payment_Id; return false; } - // Pad short pid with zeros - if (info.has_payment_id) - { - memcpy(payment_id.data, info.payment_id.data, 8); - } - - bool r = m_wallet->m_wallet->add_address_book_row(info.address,payment_id,description,info.is_subaddress); + bool r = m_wallet->m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL,description,info.is_subaddress); if (r) refresh(); else @@ -104,19 +81,12 @@ void AddressBookImpl::refresh() for (size_t i = 0; i < rows.size(); ++i) { tools::wallet2::address_book_row * row = &rows.at(i); - std::string payment_id = (row->m_payment_id == crypto::null_hash)? "" : epee::string_tools::pod_to_hex(row->m_payment_id); - std::string address = cryptonote::get_account_address_as_str(m_wallet->m_wallet->nettype(), row->m_is_subaddress, row->m_address); - // convert the zero padded short payment id to integrated address - if (!row->m_is_subaddress && payment_id.length() > 16 && payment_id.substr(16).find_first_not_of('0') == std::string::npos) { - payment_id = payment_id.substr(0,16); - crypto::hash8 payment_id_short; - if(tools::wallet2::parse_short_payment_id(payment_id, payment_id_short)) { - address = cryptonote::get_account_integrated_address_as_str(m_wallet->m_wallet->nettype(), row->m_address, payment_id_short); - // Don't show payment id when integrated address is used - payment_id = ""; - } - } - AddressBookRow * abr = new AddressBookRow(i, address, payment_id, row->m_description); + std::string address; + if (row->m_has_payment_id) + address = cryptonote::get_account_integrated_address_as_str(m_wallet->m_wallet->nettype(), row->m_address, row->m_payment_id); + else + address = get_account_address_as_str(m_wallet->m_wallet->nettype(), row->m_is_subaddress, row->m_address); + AddressBookRow * abr = new AddressBookRow(i, address, "", row->m_description); m_rows.push_back(abr); } diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 005b0bafa..f3698b599 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -78,6 +78,10 @@ void NodeRPCProxy::invalidate() m_rpc_payment_seed_hash = crypto::null_hash; m_rpc_payment_next_seed_hash = crypto::null_hash; m_height_time = 0; + m_rpc_payment_diff = 0; + m_rpc_payment_credits_per_hash_found = 0; + m_rpc_payment_height = 0; + m_rpc_payment_cookie = 0; } boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e60c6b7e1..6a622d953 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1038,10 +1038,15 @@ uint64_t gamma_picker::pick() return first_rct + crypto::rand_idx(n_rct); }; +boost::mutex wallet_keys_unlocker::lockers_lock; +unsigned int wallet_keys_unlocker::lockers = 0; wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password): w(w), locked(password != boost::none) { + boost::lock_guard<boost::mutex> lock(lockers_lock); + if (lockers++ > 0) + locked = false; if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only()) { locked = false; @@ -1056,6 +1061,9 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee:: w(w), locked(locked) { + boost::lock_guard<boost::mutex> lock(lockers_lock); + if (lockers++ > 0) + locked = false; if (!locked) return; w.generate_chacha_key_from_password(password, key); @@ -1064,9 +1072,19 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee:: wallet_keys_unlocker::~wallet_keys_unlocker() { - if (!locked) - return; - try { w.encrypt_keys(key); } + try + { + boost::lock_guard<boost::mutex> lock(lockers_lock); + if (lockers == 0) + { + MERROR("There are no lockers in wallet_keys_unlocker dtor"); + return; + } + --lockers; + if (!locked) + return; + w.encrypt_keys(key); + } catch (...) { MERROR("Failed to re-encrypt wallet keys"); @@ -1824,7 +1842,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // (that is, the prunable stuff may or may not be included) if (!miner_tx && !pool) process_unconfirmed(txid, tx, height); - std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index + + // per receiving subaddress index + std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; + std::unordered_map<cryptonote::subaddress_index, amounts_container> tx_amounts_individual_outs; + crypto::public_key tx_pub_key = null_pkey; bool notify = false; @@ -1953,6 +1975,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + if (!tx_scan_info[i].error) + { + tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered); + } } } } @@ -1976,6 +2002,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + if (!tx_scan_info[i].error) + { + tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered); + } } } } @@ -1992,6 +2022,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote hwdev.set_mode(hw::device::NONE); hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + if (!tx_scan_info[i].error) + { + tx_amounts_individual_outs[tx_scan_info[i].received->index].push_back(tx_scan_info[i].money_transfered); + } } } } @@ -2100,6 +2134,12 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info[o].received->index] < tx_scan_info[o].amount, error::wallet_internal_error, "Unexpected values of new and old outputs"); tx_money_got_in_outs[tx_scan_info[o].received->index] -= tx_scan_info[o].amount; + + amounts_container& tx_amounts_this_out = tx_amounts_individual_outs[tx_scan_info[o].received->index]; // Only for readability on the following lines + auto amount_iterator = std::find(tx_amounts_this_out.begin(), tx_amounts_this_out.end(), tx_scan_info[o].amount); + THROW_WALLET_EXCEPTION_IF(amount_iterator == tx_amounts_this_out.end(), + error::wallet_internal_error, "Unexpected values of new and old outputs"); + tx_amounts_this_out.erase(amount_iterator); } else { @@ -2165,6 +2205,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } } + THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs.size() != tx_amounts_individual_outs.size(), error::wallet_internal_error, "Inconsistent size of output arrays"); + uint64_t tx_money_spent_in_ins = 0; // The line below is equivalent to "boost::optional<uint32_t> subaddr_account;", but avoids the GCC warning: ‘*((void*)& subaddr_account +4)’ may be used uninitialized in this function // It's a GCC bug with boost::optional, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47679 @@ -2268,6 +2310,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (subaddr_account && i->first.major == *subaddr_account) { sub_change += i->second; + tx_amounts_individual_outs.erase(i->first); i = tx_money_got_in_outs.erase(i); } else @@ -2345,6 +2388,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote payment.m_tx_hash = txid; payment.m_fee = fee; payment.m_amount = i.second; + payment.m_amounts = tx_amounts_individual_outs[i.first]; payment.m_block_height = height; payment.m_unlock_time = tx.unlock_time; payment.m_timestamp = ts; @@ -3133,11 +3177,12 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, } -bool wallet2::add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress) +bool wallet2::add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress) { wallet2::address_book_row a; a.m_address = address; - a.m_payment_id = payment_id; + a.m_has_payment_id = !!payment_id; + a.m_payment_id = payment_id ? *payment_id : crypto::null_hash8; a.m_description = description; a.m_is_subaddress = is_subaddress; @@ -3148,11 +3193,12 @@ bool wallet2::add_address_book_row(const cryptonote::account_public_address &add return false; } -bool wallet2::set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress) +bool wallet2::set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress) { wallet2::address_book_row a; a.m_address = address; - a.m_payment_id = payment_id; + a.m_has_payment_id = !!payment_id; + a.m_payment_id = payment_id ? *payment_id : crypto::null_hash8; a.m_description = description; a.m_is_subaddress = is_subaddress; @@ -4236,7 +4282,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); if(!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD) r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); - THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); if (r) setup_keys(password); @@ -5394,6 +5440,7 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) void wallet2::set_offline(bool offline) { m_offline = offline; + m_node_rpc_proxy.set_offline(offline); m_http_client.set_auto_connect(!offline); if (offline) { @@ -7555,6 +7602,8 @@ bool wallet2::is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) bool wallet2::lock_keys_file() { + if (m_wallet_file.empty()) + return true; if (m_keys_file_locker) { MDEBUG(m_keys_file << " is already locked."); @@ -7566,6 +7615,8 @@ bool wallet2::lock_keys_file() bool wallet2::unlock_keys_file() { + if (m_wallet_file.empty()) + return true; if (!m_keys_file_locker) { MDEBUG(m_keys_file << " is already unlocked."); @@ -7577,6 +7628,8 @@ bool wallet2::unlock_keys_file() bool wallet2::is_keys_file_locked() const { + if (m_wallet_file.empty()) + return false; return m_keys_file_locker->locked(); } @@ -11802,13 +11855,27 @@ void wallet2::set_account_tag_description(const std::string& tag, const std::str m_account_tags.first[tag] = description; } -std::string wallet2::sign(const std::string &data) const +std::string wallet2::sign(const std::string &data, cryptonote::subaddress_index index) const { crypto::hash hash; crypto::cn_fast_hash(data.data(), data.size(), hash); const cryptonote::account_keys &keys = m_account.get_keys(); crypto::signature signature; - crypto::generate_signature(hash, keys.m_account_address.m_spend_public_key, keys.m_spend_secret_key, signature); + crypto::secret_key skey; + crypto::public_key pkey; + if (index.is_zero()) + { + skey = keys.m_spend_secret_key; + pkey = keys.m_account_address.m_spend_public_key; + } + else + { + skey = keys.m_spend_secret_key; + crypto::secret_key m = m_account.get_device().get_subaddress_secret_key(keys.m_view_secret_key, index); + sc_add((unsigned char*)&skey, (unsigned char*)&m, (unsigned char*)&skey); + secret_key_to_public_key(skey, pkey); + } + crypto::generate_signature(hash, pkey, skey, signature); return std::string("SigV1") + tools::base58::encode(std::string((const char *)&signature, sizeof(signature))); } @@ -13601,4 +13668,22 @@ std::vector<cryptonote::public_node> wallet2::get_public_nodes(bool white_only) std::copy(res.gray.begin(), res.gray.end(), std::back_inserter(nodes)); return nodes; } +//---------------------------------------------------------------------------------------------------- +std::pair<size_t, uint64_t> wallet2::estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size) +{ + THROW_WALLET_EXCEPTION_IF(n_inputs <= 0, tools::error::wallet_internal_error, "Invalid n_inputs"); + THROW_WALLET_EXCEPTION_IF(n_outputs < 0, tools::error::wallet_internal_error, "Invalid n_outputs"); + THROW_WALLET_EXCEPTION_IF(ring_size < 0, tools::error::wallet_internal_error, "Invalid ring size"); + + if (ring_size == 0) + ring_size = get_min_ring_size(); + if (n_outputs == 1) + n_outputs = 2; // extra dummy output + + const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); + size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof); + uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof); + return std::make_pair(size, weight); +} +//---------------------------------------------------------------------------------------------------- } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c86315f7c..810c002fe 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -122,6 +122,8 @@ private: wallet2 &w; bool locked; crypto::chacha_key key; + static boost::mutex lockers_lock; + static unsigned int lockers; }; class i_wallet2_callback @@ -358,10 +360,12 @@ private: END_SERIALIZE() }; + typedef std::vector<uint64_t> amounts_container; struct payment_details { crypto::hash m_tx_hash; uint64_t m_amount; + amounts_container m_amounts; uint64_t m_fee; uint64_t m_block_height; uint64_t m_unlock_time; @@ -544,9 +548,10 @@ private: struct address_book_row { cryptonote::account_public_address m_address; - crypto::hash m_payment_id; + crypto::hash8 m_payment_id; std::string m_description; bool m_is_subaddress; + bool m_has_payment_id; }; struct reserve_proof_entry @@ -1123,8 +1128,8 @@ private: * \brief GUI Address book get/store */ std::vector<address_book_row> get_address_book() const { return m_address_book; } - bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress); - bool set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress); + bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress); + bool set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress); bool delete_address_book_row(std::size_t row_id); uint64_t get_num_rct_outputs(); @@ -1182,7 +1187,7 @@ private: */ void set_account_tag_description(const std::string& tag, const std::string& description); - std::string sign(const std::string &data) const; + std::string sign(const std::string &data, cryptonote::subaddress_index index = {0, 0}) const; bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const; /*! @@ -1252,6 +1257,8 @@ private: bool is_unattended() const { return m_unattended; } + std::pair<size_t, uint64_t> estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size); + bool get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &hashing_blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie); bool daemon_requires_payment(); bool make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credits, uint64_t &balance); @@ -1626,11 +1633,11 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1) -BOOST_CLASS_VERSION(tools::wallet2::payment_details, 4) +BOOST_CLASS_VERSION(tools::wallet2::payment_details, 5) BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8) BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6) -BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17) +BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 18) BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0) BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0) BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1) @@ -1937,6 +1944,9 @@ namespace boost return; } a & x.m_coinbase; + if (ver < 5) + return; + a & x.m_amounts; } template <class Archive> @@ -1950,7 +1960,26 @@ namespace boost inline void serialize(Archive& a, tools::wallet2::address_book_row& x, const boost::serialization::version_type ver) { a & x.m_address; - a & x.m_payment_id; + if (ver < 18) + { + crypto::hash payment_id; + a & payment_id; + x.m_has_payment_id = !(payment_id == crypto::null_hash); + if (x.m_has_payment_id) + { + bool is_long = false; + for (int i = 8; i < 32; ++i) + is_long |= payment_id.data[i]; + if (is_long) + { + MWARNING("Long payment ID ignored on address book load"); + x.m_payment_id = crypto::null_hash8; + x.m_has_payment_id = false; + } + else + memcpy(x.m_payment_id.data, payment_id.data, 8); + } + } a & x.m_description; if (ver < 17) { @@ -1958,6 +1987,11 @@ namespace boost return; } a & x.m_is_subaddress; + if (ver < 18) + return; + a & x.m_has_payment_id; + if (x.m_has_payment_id) + a & x.m_payment_id; } template <class Archive> diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de501f056..2a051553b 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -326,6 +326,7 @@ namespace tools entry.height = pd.m_block_height; entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; + entry.amounts = pd.m_amounts; entry.unlock_time = pd.m_unlock_time; entry.locked = !m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height); entry.fee = pd.m_fee; @@ -408,6 +409,7 @@ namespace tools entry.height = 0; entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; + entry.amounts = pd.m_amounts; entry.unlock_time = pd.m_unlock_time; entry.locked = true; entry.fee = pd.m_fee; @@ -550,9 +552,29 @@ namespace tools if (!m_wallet) return not_open(er); try { - m_wallet->add_subaddress(req.account_index, req.label); - res.address_index = m_wallet->get_num_subaddresses(req.account_index) - 1; - res.address = m_wallet->get_subaddress_as_str({req.account_index, res.address_index}); + if (req.count < 1 || req.count > 64) { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Count must be between 1 and 64."; + return false; + } + + std::vector<std::string> addresses; + std::vector<uint32_t> address_indices; + + addresses.reserve(req.count); + address_indices.reserve(req.count); + + for (uint32_t i = 0; i < req.count; i++) { + m_wallet->add_subaddress(req.account_index, req.label); + uint32_t new_address_index = m_wallet->get_num_subaddresses(req.account_index) - 1; + address_indices.push_back(new_address_index); + addresses.push_back(m_wallet->get_subaddress_as_str({req.account_index, new_address_index})); + } + + res.address = addresses[0]; + res.address_index = address_indices[0]; + res.addresses = addresses; + res.address_indices = address_indices; } catch (const std::exception& e) { @@ -843,7 +865,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ template<typename Ts, typename Tu> bool wallet_rpc_server::fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector, - bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, + bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, Tu &weight, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er) { for (const auto & ptx : ptx_vector) @@ -858,6 +880,7 @@ namespace tools // Compute amount leaving wallet in tx. By convention dests does not include change outputs fill(amount, total_amount(ptx)); fill(fee, ptx.fee); + fill(weight, cryptonote::get_transaction_weight(ptx.tx)); } if (m_wallet->multisig()) @@ -943,7 +966,7 @@ namespace tools return false; } - 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, + return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.weight, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er); } catch (const std::exception& e) @@ -989,7 +1012,7 @@ namespace tools return false; } - 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, + return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); } catch (const std::exception& e) @@ -1353,7 +1376,7 @@ namespace tools { std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(); - 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, + return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); } catch (const std::exception& e) @@ -1400,7 +1423,7 @@ namespace tools uint32_t priority = m_wallet->adjust_priority(req.priority); 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, req.subaddr_indices); - 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, + return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); } catch (const std::exception& e) @@ -1475,7 +1498,7 @@ namespace tools return false; } - 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, + return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.weight, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er); } catch (const std::exception& e) @@ -1943,7 +1966,7 @@ namespace tools return false; } - res.signature = m_wallet->sign(req.data); + res.signature = m_wallet->sign(req.data, {req.account_index, req.address_index}); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -2725,7 +2748,14 @@ namespace tools { uint64_t idx = 0; for (const auto &entry: ab) - res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); + { + std::string address; + if (entry.m_has_payment_id) + address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id); + else + address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address); + res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, address, entry.m_description}); + } } else { @@ -2738,7 +2768,12 @@ namespace tools return false; } const auto &entry = ab[idx]; - res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); + std::string address; + if (entry.m_has_payment_id) + address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id); + else + address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address); + res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, address, entry.m_description}); } } return true; @@ -2755,7 +2790,6 @@ namespace tools } cryptonote::address_parse_info info; - crypto::hash payment_id = crypto::null_hash; er.message = ""; if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), req.address, [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string { @@ -2777,39 +2811,7 @@ namespace tools er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address; return false; } - if (info.has_payment_id) - { - memcpy(payment_id.data, info.payment_id.data, 8); - memset(payment_id.data + 8, 0, 24); - } - if (!req.payment_id.empty()) - { - if (info.has_payment_id) - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Separate payment ID given with integrated address"; - return false; - } - - crypto::hash long_payment_id; - - if (!wallet2::parse_long_payment_id(req.payment_id, payment_id)) - { - if (!wallet2::parse_short_payment_id(req.payment_id, info.payment_id)) - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 64 character string"; - return false; - } - else - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address"; - return false; - } - } - } - if (!m_wallet->add_address_book_row(info.address, payment_id, req.description, info.is_subaddress)) + if (!m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL, req.description, info.is_subaddress)) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Failed to add address book entry"; @@ -2840,7 +2842,6 @@ namespace tools tools::wallet2::address_book_row entry = ab[req.index]; cryptonote::address_parse_info info; - crypto::hash payment_id = crypto::null_hash; if (req.set_address) { er.message = ""; @@ -2867,52 +2868,13 @@ namespace tools entry.m_address = info.address; entry.m_is_subaddress = info.is_subaddress; if (info.has_payment_id) - { - memcpy(entry.m_payment_id.data, info.payment_id.data, 8); - memset(entry.m_payment_id.data + 8, 0, 24); - } - } - - if (req.set_payment_id) - { - if (req.payment_id.empty()) - { - payment_id = crypto::null_hash; - } - else - { - if (req.set_address && info.has_payment_id) - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Separate payment ID given with integrated address"; - return false; - } - - if (!wallet2::parse_long_payment_id(req.payment_id, payment_id)) - { - crypto::hash8 spid; - if (!wallet2::parse_short_payment_id(req.payment_id, spid)) - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 64 character string"; - return false; - } - else - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address"; - return false; - } - } - } - - entry.m_payment_id = payment_id; + entry.m_payment_id = info.payment_id; } if (req.set_description) entry.m_description = req.description; - if (!m_wallet->set_address_book_row(req.index, entry.m_address, entry.m_payment_id, entry.m_description, entry.m_is_subaddress)) + if (!m_wallet->set_address_book_row(req.index, entry.m_address, req.set_address && entry.m_has_payment_id ? &entry.m_payment_id : NULL, entry.m_description, entry.m_is_subaddress)) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Failed to edit address book entry"; @@ -4291,6 +4253,25 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); + try + { + size_t extra_size = 34 /* pubkey */ + 10 /* encrypted payment id */; // typical makeup + const std::pair<size_t, uint64_t> sw = m_wallet->estimate_tx_size_and_weight(req.rct, req.n_inputs, req.ring_size, req.n_outputs, extra_size); + res.size = sw.first; + res.weight = sw.second; + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to determine size and weight"; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx) { res.version = WALLET_RPC_VERSION; diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index b2b5e7116..89bf3a924 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -154,6 +154,7 @@ namespace tools MAP_JON_RPC_WE("set_daemon", on_set_daemon, wallet_rpc::COMMAND_RPC_SET_DAEMON) MAP_JON_RPC_WE("set_log_level", on_set_log_level, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL) MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES) + MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT) MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) END_JSON_RPC_MAP() END_URI_MAP2() @@ -240,6 +241,7 @@ namespace tools bool on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_set_log_level(const wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); //json rpc v2 @@ -255,7 +257,7 @@ namespace tools template<typename Ts, typename Tu> bool fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector, - bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, + bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, Tu &weight, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er); bool 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); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 0c86f404d..614e7af08 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 16 +#define WALLET_RPC_VERSION_MINOR 17 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -182,11 +182,13 @@ namespace wallet_rpc { struct request_t { - uint32_t account_index; + uint32_t account_index; + uint32_t count; std::string label; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(account_index) + KV_SERIALIZE_OPT(count, 1U) KV_SERIALIZE(label) END_KV_SERIALIZE_MAP() }; @@ -194,12 +196,16 @@ namespace wallet_rpc struct response_t { - std::string address; - uint32_t address_index; + std::string address; + uint32_t address_index; + std::vector<std::string> addresses; + std::vector<uint32_t> address_indices; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) KV_SERIALIZE(address_index) + KV_SERIALIZE(addresses) + KV_SERIALIZE(address_indices) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -484,6 +490,7 @@ namespace wallet_rpc std::string tx_key; uint64_t amount; uint64_t fee; + uint64_t weight; std::string tx_blob; std::string tx_metadata; std::string multisig_txset; @@ -494,6 +501,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) KV_SERIALIZE(fee) + KV_SERIALIZE(weight) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) @@ -550,6 +558,7 @@ namespace wallet_rpc std::list<std::string> tx_key_list; std::list<uint64_t> amount_list; std::list<uint64_t> fee_list; + std::list<uint64_t> weight_list; std::list<std::string> tx_blob_list; std::list<std::string> tx_metadata_list; std::string multisig_txset; @@ -560,6 +569,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_key_list) KV_SERIALIZE(amount_list) KV_SERIALIZE(fee_list) + KV_SERIALIZE(weight_list) KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) @@ -723,6 +733,7 @@ namespace wallet_rpc std::list<std::string> tx_key_list; std::list<uint64_t> amount_list; std::list<uint64_t> fee_list; + std::list<uint64_t> weight_list; std::list<std::string> tx_blob_list; std::list<std::string> tx_metadata_list; std::string multisig_txset; @@ -733,6 +744,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_key_list) KV_SERIALIZE(amount_list) KV_SERIALIZE(fee_list) + KV_SERIALIZE(weight_list) KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) @@ -793,6 +805,7 @@ namespace wallet_rpc std::list<std::string> tx_key_list; std::list<uint64_t> amount_list; std::list<uint64_t> fee_list; + std::list<uint64_t> weight_list; std::list<std::string> tx_blob_list; std::list<std::string> tx_metadata_list; std::string multisig_txset; @@ -803,6 +816,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_key_list) KV_SERIALIZE(amount_list) KV_SERIALIZE(fee_list) + KV_SERIALIZE(weight_list) KV_SERIALIZE(tx_blob_list) KV_SERIALIZE(tx_metadata_list) KV_SERIALIZE(multisig_txset) @@ -850,6 +864,7 @@ namespace wallet_rpc std::string tx_key; uint64_t amount; uint64_t fee; + uint64_t weight; std::string tx_blob; std::string tx_metadata; std::string multisig_txset; @@ -860,6 +875,7 @@ namespace wallet_rpc KV_SERIALIZE(tx_key) KV_SERIALIZE(amount) KV_SERIALIZE(fee) + KV_SERIALIZE(weight) KV_SERIALIZE(tx_blob) KV_SERIALIZE(tx_metadata) KV_SERIALIZE(multisig_txset) @@ -1354,6 +1370,7 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init<response_t> response; }; + typedef std::vector<uint64_t> amounts_container; struct transfer_entry { std::string txid; @@ -1361,6 +1378,7 @@ namespace wallet_rpc uint64_t height; uint64_t timestamp; uint64_t amount; + amounts_container amounts; uint64_t fee; std::string note; std::list<transfer_destination> destinations; @@ -1380,6 +1398,7 @@ namespace wallet_rpc KV_SERIALIZE(height); KV_SERIALIZE(timestamp); KV_SERIALIZE(amount); + KV_SERIALIZE(amounts); KV_SERIALIZE(fee); KV_SERIALIZE(note); KV_SERIALIZE(destinations); @@ -1591,9 +1610,13 @@ namespace wallet_rpc struct request_t { std::string data; + uint32_t account_index; + uint32_t address_index; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(data); + KV_SERIALIZE(data) + KV_SERIALIZE_OPT(account_index, 0u) + KV_SERIALIZE_OPT(address_index, 0u) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; @@ -1823,12 +1846,10 @@ namespace wallet_rpc struct request_t { std::string address; - std::string payment_id; std::string description; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) - KV_SERIALIZE(payment_id) KV_SERIALIZE(description) END_KV_SERIALIZE_MAP() }; @@ -1852,8 +1873,6 @@ namespace wallet_rpc uint64_t index; bool set_address; std::string address; - bool set_payment_id; - std::string payment_id; bool set_description; std::string description; @@ -1861,8 +1880,6 @@ namespace wallet_rpc KV_SERIALIZE(index) KV_SERIALIZE(set_address) KV_SERIALIZE(address) - KV_SERIALIZE(set_payment_id) - KV_SERIALIZE(payment_id) KV_SERIALIZE(set_description) KV_SERIALIZE(description) END_KV_SERIALIZE_MAP() @@ -1893,13 +1910,11 @@ namespace wallet_rpc { uint64_t index; std::string address; - std::string payment_id; std::string description; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(index) KV_SERIALIZE(address) - KV_SERIALIZE(payment_id) KV_SERIALIZE(description) END_KV_SERIALIZE_MAP() }; @@ -2580,5 +2595,36 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init<response_t> response; }; + struct COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT + { + struct request_t + { + uint32_t n_inputs; + uint32_t n_outputs; + uint32_t ring_size; + bool rct; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(n_inputs) + KV_SERIALIZE(n_outputs) + KV_SERIALIZE_OPT(ring_size, 0u) + KV_SERIALIZE_OPT(rct, true) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + uint64_t size; + uint64_t weight; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(size) + KV_SERIALIZE(weight) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + } } |