diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/api/wallet.cpp | 20 | ||||
-rw-r--r-- | src/wallet/api/wallet.h | 2 | ||||
-rw-r--r-- | src/wallet/api/wallet2_api.h | 8 | ||||
-rw-r--r-- | src/wallet/node_rpc_proxy.cpp | 4 | ||||
-rw-r--r-- | src/wallet/ringdb.cpp | 55 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 194 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 22 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 35 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.h | 2 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server_commands_defs.h | 21 |
10 files changed, 294 insertions, 69 deletions
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 6200c7a1f..33047f703 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1671,6 +1671,26 @@ void WalletImpl::disposeTransaction(PendingTransaction *t) delete t; } +uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations, + PendingTransaction::Priority priority) const +{ + const size_t pubkey_size = 33; + const size_t encrypted_paymentid_size = 11; + const size_t extra_size = pubkey_size + encrypted_paymentid_size; + + return m_wallet->estimate_fee( + m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0), + m_wallet->use_fork_rules(4, 0), + 1, + m_wallet->get_min_ring_size() - 1, + destinations.size() + 1, + extra_size, + m_wallet->use_fork_rules(8, 0), + m_wallet->get_base_fee(), + m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast<uint32_t>(priority))), + m_wallet->get_fee_quantization_mask()); +} + TransactionHistory *WalletImpl::history() { return m_history.get(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 331bf4b38..e46a62340 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -166,6 +166,8 @@ public: bool importKeyImages(const std::string &filename) override; virtual void disposeTransaction(PendingTransaction * t) override; + virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations, + PendingTransaction::Priority priority) const override; virtual TransactionHistory * history() override; virtual AddressBook * addressBook() override; virtual Subaddress * subaddress() override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index e543a115b..3945e55c9 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -879,6 +879,14 @@ struct Wallet */ virtual void disposeTransaction(PendingTransaction * t) = 0; + /*! + * \brief Estimates transaction fee. + * \param destinations Vector consisting of <address, amount> pairs. + * \return Estimated fee. + */ + virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations, + PendingTransaction::Priority priority) const = 0; + /*! * \brief exportKeyImages - exports key images to file * \param filename 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/ringdb.cpp b/src/wallet/ringdb.cpp index b7efdd75c..5e88ea788 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -39,6 +39,8 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb" +#define V1TAG ((uint64_t)798237759845202) + static const char zerokey[8] = {0}; static const MDB_val zerokeyval = { sizeof(zerokey), (void *)zerokey }; @@ -63,15 +65,16 @@ static int compare_uint64(const MDB_val *a, const MDB_val *b) return va < vb ? -1 : va > vb; } -static std::string compress_ring(const std::vector<uint64_t> &ring) +static std::string compress_ring(const std::vector<uint64_t> &ring, uint64_t tag) { std::string s; + s += tools::get_varint_data(tag); for (uint64_t out: ring) s += tools::get_varint_data(out); return s; } -static std::vector<uint64_t> decompress_ring(const std::string &s) +static std::vector<uint64_t> decompress_ring(const std::string &s, uint64_t tag) { std::vector<uint64_t> ring; int read = 0; @@ -81,6 +84,13 @@ static std::vector<uint64_t> decompress_ring(const std::string &s) std::string tmp(i, s.cend()); read = tools::read_varint(tmp.begin(), tmp.end(), out); THROW_WALLET_EXCEPTION_IF(read <= 0 || read > 256, tools::error::wallet_internal_error, "Internal error decompressing ring"); + if (tag) + { + if (tag != out) + return {}; + tag = 0; + continue; + } ring.push_back(out); } return ring; @@ -93,25 +103,27 @@ std::string get_rings_filename(boost::filesystem::path filename) return filename.string(); } -static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key) +static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { static const char salt[] = "ringdsb"; - uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt)]; + uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt) + sizeof(field)]; memcpy(buffer, &key_image, sizeof(key_image)); memcpy(buffer + sizeof(key_image), &key, sizeof(key)); memcpy(buffer + sizeof(key_image) + sizeof(key), salt, sizeof(salt)); + memcpy(buffer + sizeof(key_image) + sizeof(key) + sizeof(salt), &field, sizeof(field)); crypto::hash hash; - crypto::cn_fast_hash(buffer, sizeof(buffer), hash.data); + // if field is 0, backward compat mode: hash without the field + crypto::cn_fast_hash(buffer, sizeof(buffer) - !field, hash.data); static_assert(sizeof(hash) >= CHACHA_IV_SIZE, "Incompatible hash and chacha IV sizes"); crypto::chacha_iv iv; memcpy(&iv, &hash, CHACHA_IV_SIZE); return iv; } -static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - const crypto::chacha_iv iv = make_iv(key_image, key); + const crypto::chacha_iv iv = make_iv(key_image, key, field); std::string ciphertext; ciphertext.resize(plaintext.size() + sizeof(iv)); crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]); @@ -119,14 +131,14 @@ static std::string encrypt(const std::string &plaintext, const crypto::key_image return ciphertext; } -static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key); + return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key, field); } -static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key) +static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field) { - const crypto::chacha_iv iv = make_iv(key_image, key); + const crypto::chacha_iv iv = make_iv(key_image, key, field); std::string plaintext; THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(iv), tools::error::wallet_internal_error, "Bad ciphertext text"); plaintext.resize(ciphertext.size() - sizeof(iv)); @@ -137,11 +149,11 @@ static std::string decrypt(const std::string &ciphertext, const crypto::key_imag static void store_relative_ring(MDB_txn *txn, MDB_dbi &dbi, const crypto::key_image &key_image, const std::vector<uint64_t> &relative_ring, const crypto::chacha_key &chacha_key) { MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); - std::string compressed_ring = compress_ring(relative_ring); - std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key); + std::string compressed_ring = compress_ring(relative_ring, V1TAG); + std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key, 1); data.mv_size = data_ciphertext.size(); data.mv_data = (void*)data_ciphertext.c_str(); int dbr = mdb_put(txn, dbi, &key, &data, 0); @@ -297,7 +309,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vecto for (const crypto::key_image &key_image: key_images) { MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); @@ -349,7 +361,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im tx_active = true; MDB_val key, data; - std::string key_ciphertext = encrypt(key_image, chacha_key); + std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); key.mv_size = key_ciphertext.size(); dbr = mdb_get(txn, dbi_rings, &key, &data); @@ -358,8 +370,15 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return false; THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size"); - std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key); - outs = decompress_ring(data_plaintext); + bool try_v0 = false; + std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 1); + try { outs = decompress_ring(data_plaintext, V1TAG); if (outs.empty()) try_v0 = true; } + catch(...) { try_v0 = true; } + if (try_v0) + { + data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 0); + outs = decompress_ring(data_plaintext, 0); + } MDEBUG("Found ring for key image " << key_image << ":"); MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); outs = cryptonote::relative_output_offsets_to_absolute(outs); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 438c61370..6d2fff17b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -44,6 +44,7 @@ using namespace epee; #include "cryptonote_config.h" +#include "cryptonote_core/tx_sanity_check.h" #include "wallet_rpc_helpers.h" #include "wallet2.h" #include "cryptonote_basic/cryptonote_format_utils.h" @@ -149,6 +150,9 @@ static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1"; static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1"; +boost::mutex tools::wallet2::default_daemon_address_lock; +std::string tools::wallet2::default_daemon_address = ""; + namespace { std::string get_default_ringdb_path() @@ -412,6 +416,15 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl daemon_port = get_config(nettype).RPC_DEFAULT_PORT; } + // if no daemon settings are given and we have a previous one, reuse that one + if (command_line::is_arg_defaulted(vm, opts.daemon_host) && command_line::is_arg_defaulted(vm, opts.daemon_port) && command_line::is_arg_defaulted(vm, opts.daemon_address)) + { + // not a bug: taking a const ref to a temporary in this way is actually ok in a recent C++ standard + const std::string &def = tools::wallet2::get_default_daemon_address(); + if (!def.empty()) + daemon_address = def; + } + if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); @@ -591,6 +604,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f } viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data()); crypto::public_key pkey; + if (viewkey == crypto::null_skey) + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view secret key may not be all zeroes")); if (!crypto::secret_key_to_public_key(viewkey, pkey)) { THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key")); } @@ -607,6 +622,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f } spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data()); crypto::public_key pkey; + if (spendkey == crypto::null_skey) + THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend secret key may not be all zeroes")); if (!crypto::secret_key_to_public_key(spendkey, pkey)) { THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key")); } @@ -879,20 +896,6 @@ uint8_t get_bulletproof_fork() return 8; } -uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) -{ - if (use_per_byte_fee) - { - const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); - return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask); - } - else - { - const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); - return calculate_fee(base_fee, estimated_tx_size, fee_multiplier); - } -} - uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) { if (use_per_byte_fee) @@ -1186,6 +1189,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_ringdb(), m_last_block_reward(0), m_encrypt_keys_after_refresh(boost::none), + m_decrypt_keys_lockers(0), m_unattended(unattended), m_devices_registered(false), m_device_last_key_image_sync(0), @@ -1312,8 +1316,15 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u m_node_rpc_proxy.invalidate(); } - MINFO("setting daemon to " << get_daemon_address()); - return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options)); + const std::string address = get_daemon_address(); + MINFO("setting daemon to " << address); + bool ret = m_http_client.set_server(address, get_daemon_login(), std::move(ssl_options)); + if (ret) + { + CRITICAL_REGION_LOCAL(default_daemon_address_lock); + default_daemon_address = address; + } + return ret; } //---------------------------------------------------------------------------------------------------- bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options) @@ -1842,7 +1853,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; @@ -1971,6 +1986,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); + } } } } @@ -1994,6 +2013,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); + } } } } @@ -2010,6 +2033,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); + } } } } @@ -2118,6 +2145,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 { @@ -2183,6 +2216,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 @@ -2286,6 +2321,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 @@ -2363,6 +2399,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; @@ -2847,7 +2884,7 @@ void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashe } //---------------------------------------------------------------------------------------------------- -void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed) +void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed) { MTRACE("update_pool_state start"); @@ -2907,7 +2944,6 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b pit->second.m_state = wallet2::unconfirmed_transfer_details::failed; // the inputs aren't spent anymore, since the tx failed - remove_rings(pit->second.m_tx); for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini) { if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key)) @@ -3037,7 +3073,7 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b [tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; }); if (i != txids.end()) { - process_txs.push_back(std::make_pair(tx, tx_entry.double_spend_seen)); + process_txs.push_back(std::make_tuple(tx, tx_hash, tx_entry.double_spend_seen)); } else { @@ -3068,14 +3104,14 @@ void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, b MTRACE("update_pool_state end"); } //---------------------------------------------------------------------------------------------------- -void wallet2::process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs) +void wallet2::process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs) { const time_t now = time(NULL); for (const auto &e: txs) { - const cryptonote::transaction &tx = e.first; - const bool double_spend_seen = e.second; - const crypto::hash tx_hash = get_transaction_hash(tx); + const cryptonote::transaction &tx = std::get<0>(e); + const crypto::hash &tx_hash = std::get<1>(e); + const bool double_spend_seen = std::get<2>(e); process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, 0, now, false, true, double_spend_seen, {}); m_scanned_pool_txs[0].insert(tx_hash); if (m_scanned_pool_txs[0].size() > 5000) @@ -3114,6 +3150,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, MERROR("Blocks start before blockchain offset: " << blocks_start_height << " " << m_blockchain.offset()); return; } + current_index = blocks_start_height; if (hashes.size() + current_index < stop_height) { drop_from_short_history(short_chain_history, 3); std::vector<crypto::hash>::iterator right = hashes.end(); @@ -3123,7 +3160,6 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, short_chain_history.push_front(*right); } } - current_index = blocks_start_height; for(auto& bl_id: hashes) { if(current_index >= m_blockchain.size()) @@ -3296,7 +3332,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo // since that might cause a password prompt, which would introduce a data // leak allowing a passive adversary with traffic analysis capability to // infer when we get an incoming output - std::vector<std::pair<cryptonote::transaction, bool>> process_pool_txs; + std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_pool_txs; update_pool_state(process_pool_txs, true); bool first = true, last = false; @@ -4061,9 +4097,18 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_always_confirm_transfers = field_always_confirm_transfers; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, print_ring_members, int, Int, false, true); m_print_ring_members = field_print_ring_members; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true); - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true); - m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0)); + if (json.HasMember("store_tx_info")) + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, true, true); + m_store_tx_info = field_store_tx_info; + } + else if (json.HasMember("store_tx_keys")) // backward compatibility + { + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, true, true); + m_store_tx_info = field_store_tx_keys; + } + else + m_store_tx_info = true; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0); m_default_mixin = field_default_mixin; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0); @@ -4256,7 +4301,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); @@ -4345,12 +4390,18 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip void wallet2::encrypt_keys(const crypto::chacha_key &key) { + boost::lock_guard<boost::mutex> lock(m_decrypt_keys_lock); + if (--m_decrypt_keys_lockers) // another lock left ? + return; m_account.encrypt_keys(key); m_account.decrypt_viewkey(key); } void wallet2::decrypt_keys(const crypto::chacha_key &key) { + boost::lock_guard<boost::mutex> lock(m_decrypt_keys_lock); + if (m_decrypt_keys_lockers++) // already unlocked ? + return; m_account.encrypt_viewkey(key); m_account.decrypt_keys(key); } @@ -7101,6 +7152,20 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto return sign_multisig_tx_to_file(exported_txs, filename, txids); } //---------------------------------------------------------------------------------------------------- +uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const +{ + if (use_per_byte_fee) + { + const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); + return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask); + } + else + { + const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof); + return calculate_fee(base_fee, estimated_tx_size, fee_multiplier); + } +} + uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) { static const struct @@ -7576,6 +7641,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."); @@ -7587,6 +7654,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."); @@ -7598,6 +7667,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(); } @@ -7736,8 +7807,50 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_ } } +std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs) +{ + std::set<uint64_t> unique; + size_t total = 0; + + for (const auto &it : outs) + { + for (const auto &out : it) + { + const uint64_t global_index = std::get<0>(out); + unique.insert(global_index); + } + total += it.size(); + } + + return std::make_pair(std::move(unique), total); +} + void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count) { + std::vector<uint64_t> rct_offsets; + for (size_t attempts = 3; attempts > 0; --attempts) + { + get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets); + + const auto unique = outs_unique(outs); + if (tx_sanity_check(unique.first, unique.second, rct_offsets.empty() ? 0 : rct_offsets.back())) + { + return; + } + + std::vector<crypto::key_image> key_images; + key_images.reserve(selected_transfers.size()); + std::for_each(selected_transfers.begin(), selected_transfers.end(), [this, &key_images](size_t index) { + key_images.push_back(m_transfers[index].m_key_image); + }); + unset_ring(key_images); + } + + THROW_WALLET_EXCEPTION(error::wallet_internal_error, tr("Transaction sanity check failed")); +} + +void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets) +{ LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); outs.clear(); @@ -7758,7 +7871,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // if we have at least one rct out, get the distribution, or fall back to the previous system uint64_t rct_start_height; - std::vector<uint64_t> rct_offsets; bool has_rct = false; uint64_t max_rct_index = 0; for (size_t idx: selected_transfers) @@ -7767,7 +7879,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> has_rct = true; max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index); } - const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets); + const bool has_rct_distribution = has_rct && (!rct_offsets.empty() || get_rct_distribution(rct_start_height, rct_offsets)); if (has_rct_distribution) { // check we're clear enough of rct start, to avoid corner cases below @@ -11823,13 +11935,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))); } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 1faeb1df8..1dac8acac 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -360,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; @@ -1185,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; /*! @@ -1222,8 +1224,8 @@ private: bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; - void update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed = false); - void process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs); + void update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false); + void process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs); void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes); std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const; @@ -1244,6 +1246,7 @@ private: std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels); std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees); + uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const; uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1); uint64_t get_base_fee(); uint64_t get_fee_quantization_mask(); @@ -1387,6 +1390,8 @@ private: uint64_t credits() const { return m_rpc_payment_state.credits; } void credit_report(uint64_t &expected_spent, uint64_t &discrepancy) const { expected_spent = m_rpc_payment_state.expected_spent; discrepancy = m_rpc_payment_state.discrepancy; } + static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } + private: /*! * \brief Stores wallet information to wallet file. @@ -1438,6 +1443,7 @@ private: bool is_spent(const transfer_details &td, bool strict = true) const; bool is_spent(size_t idx, bool strict = true) const; void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count); + void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets); bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const; std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const; @@ -1616,6 +1622,8 @@ private: crypto::chacha_key m_cache_key; boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh; + boost::mutex m_decrypt_keys_lock; + unsigned int m_decrypt_keys_lockers; bool m_unattended; bool m_devices_registered; @@ -1624,6 +1632,9 @@ private: std::unique_ptr<wallet_device_callback> m_device_callback; ExportFormat m_export_format; + + static boost::mutex default_daemon_address_lock; + static std::string default_daemon_address; }; } BOOST_CLASS_VERSION(tools::wallet2, 29) @@ -1631,7 +1642,7 @@ 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) @@ -1942,6 +1953,9 @@ namespace boost return; } a & x.m_coinbase; + if (ver < 5) + return; + a & x.m_amounts; } template <class Archive> diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index cb2ee3549..db2e2344b 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; @@ -863,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) @@ -878,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()) @@ -963,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) @@ -1009,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) @@ -1177,7 +1180,6 @@ namespace tools } } - std::vector<tools::wallet2::pending_tx> ptx; try { // gather info to ask the user @@ -1373,7 +1375,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) @@ -1414,13 +1416,24 @@ namespace tools return false; } + std::set<uint32_t> subaddr_indices; + if (req.subaddr_indices_all) + { + for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(req.account_index); ++i) + subaddr_indices.insert(i); + } + else + { + subaddr_indices= req.subaddr_indices; + } + try { uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0); 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); + 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); - 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) @@ -1495,7 +1508,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) @@ -1963,7 +1976,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; } //------------------------------------------------------------------------------------------------------------------------------ @@ -2458,7 +2471,7 @@ namespace tools if (req.pool) { - std::vector<std::pair<cryptonote::transaction, bool>> process_txs; + std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs; m_wallet->update_pool_state(process_txs); if (!process_txs.empty()) m_wallet->process_pool_state(process_txs); @@ -2541,7 +2554,7 @@ namespace tools } } - std::vector<std::pair<cryptonote::transaction, bool>> process_txs; + std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs; m_wallet->update_pool_state(process_txs); if (!process_txs.empty()) m_wallet->process_pool_state(process_txs); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 41f6879ef..89bf3a924 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -257,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 078e9778b..a212b79e6 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -490,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; @@ -500,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) @@ -556,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; @@ -566,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) @@ -729,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; @@ -739,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) @@ -755,6 +761,7 @@ namespace wallet_rpc std::string address; uint32_t account_index; std::set<uint32_t> subaddr_indices; + bool subaddr_indices_all; uint32_t priority; uint64_t ring_size; uint64_t outputs; @@ -770,6 +777,7 @@ namespace wallet_rpc KV_SERIALIZE(address) KV_SERIALIZE(account_index) KV_SERIALIZE(subaddr_indices) + KV_SERIALIZE_OPT(subaddr_indices_all, false) KV_SERIALIZE(priority) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) KV_SERIALIZE_OPT(outputs, (uint64_t)1) @@ -799,6 +807,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; @@ -809,6 +818,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) @@ -856,6 +866,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; @@ -866,6 +877,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) @@ -1360,6 +1372,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; @@ -1367,6 +1380,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; @@ -1386,6 +1400,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); @@ -1597,9 +1612,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; |