From 81bdea5f7a1338c4345f459b538082699d5b0fdd Mon Sep 17 00:00:00 2001 From: who-biz <37732338+who-biz@users.noreply.github.com> Date: Wed, 10 Jul 2019 09:03:30 -0400 Subject: [PATCH] Fix wallet output scanning, remove lightwallet --- src/simplewallet/simplewallet.cpp | 2 - src/wallet/api/wallet.cpp | 81 +-- src/wallet/api/wallet.h | 4 +- src/wallet/api/wallet2_api.h | 8 +- src/wallet/wallet2.cpp | 853 ++---------------------------- src/wallet/wallet2.h | 42 +- 6 files changed, 64 insertions(+), 926 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 08231569..51859246 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -260,8 +260,6 @@ namespace } refresh_type_names[] = { { "full", tools::wallet2::RefreshFull }, - { "optimize-coinbase", tools::wallet2::RefreshOptimizeCoinbase }, - { "optimized-coinbase", tools::wallet2::RefreshOptimizeCoinbase }, { "no-coinbase", tools::wallet2::RefreshNoCoinbase }, { "default", tools::wallet2::RefreshDefault }, }; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 45138e84..e531a489 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -170,37 +170,6 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback // TODO; } - // Light wallet callbacks - virtual void on_lw_new_block(uint64_t height) - { - if (m_listener) { - m_listener->newBlock(height); - } - } - - virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) - { - if (m_listener) { - std::string tx_hash = epee::string_tools::pod_to_hex(txid); - m_listener->moneyReceived(tx_hash, amount); - } - } - - virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) - { - if (m_listener) { - std::string tx_hash = epee::string_tools::pod_to_hex(txid); - m_listener->unconfirmedMoneyReceived(tx_hash, amount); - } - } - - virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) - { - if (m_listener) { - std::string tx_hash = epee::string_tools::pod_to_hex(txid); - m_listener->moneySpent(tx_hash, amount); - } - } WalletListener * m_listener; WalletImpl * m_wallet; @@ -799,47 +768,14 @@ string WalletImpl::keysFilename() const return m_wallet->get_keys_file(); } -bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username, const std::string &daemon_password, bool use_ssl, bool lightWallet) +bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username, const std::string &daemon_password, bool use_ssl) { clearStatus(); - m_wallet->set_light_wallet(lightWallet); if(daemon_username != "") m_daemon_login.emplace(daemon_username, daemon_password); return doInit(daemon_address, upper_transaction_size_limit, use_ssl); } -bool WalletImpl::lightWalletLogin(bool &isNewWallet) const -{ - return m_wallet->light_wallet_login(isNewWallet); -} - -bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) -{ - try - { - cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response response; - if(!m_wallet->light_wallet_import_wallet_request(response)){ - m_errorString = tr("Failed to send import wallet request"); - m_status = Status_Error; - return false; - } - fee = response.import_fee; - payment_id = response.payment_id; - new_request = response.new_request; - request_fulfilled = response.request_fulfilled; - payment_address = response.payment_address; - status = response.status; - } - catch (const std::exception &e) - { - LOG_ERROR("Error sending import wallet request: " << e.what()); - m_errorString = e.what(); - m_status = Status_Error; - return false; - } - return true; -} - void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height) { m_wallet->set_refresh_from_block_height(refresh_from_block_height); @@ -862,9 +798,6 @@ uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const uint64_t WalletImpl::blockChainHeight() const { - if(m_wallet->light_wallet()) { - return m_wallet->get_light_wallet_scanned_block_height(); - } return m_wallet->get_blockchain_current_height(); } uint64_t WalletImpl::approximateBlockChainHeight() const @@ -879,9 +812,6 @@ uint64_t WalletImpl::estimateBlockChainHeight() const uint64_t WalletImpl::daemonBlockChainHeight() const { - if(m_wallet->light_wallet()) { - return m_wallet->get_light_wallet_scanned_block_height(); - } if (!m_is_connected) return 0; std::string err; @@ -901,9 +831,6 @@ uint64_t WalletImpl::daemonBlockChainHeight() const uint64_t WalletImpl::daemonBlockChainTargetHeight() const { - if(m_wallet->light_wallet()) { - return m_wallet->get_light_wallet_blockchain_height(); - } if (!m_is_connected) return 0; std::string err; @@ -1630,9 +1557,6 @@ Wallet::ConnectionStatus WalletImpl::connected() const m_is_connected = m_wallet->check_connection(&version, DEFAULT_CONNECTION_TIMEOUT_MILLIS); if (!m_is_connected) return Wallet::ConnectionStatus_Disconnected; - // Version check is not implemented in light wallets nodes/wallets - if (!m_wallet->light_wallet() && (version >> 16) != CORE_RPC_VERSION_MAJOR) - return Wallet::ConnectionStatus_WrongVersion; return Wallet::ConnectionStatus_Connected; } @@ -1694,7 +1618,7 @@ void WalletImpl::doRefresh() try { // Syncing daemon and refreshing wallet simultaneously is very resource intensive. // Disable refresh if wallet is disconnected or daemon isn't synced. - if (m_wallet->light_wallet() || daemonSynced()) { + if (daemonSynced()) { m_wallet->refresh(); if (!m_synchronized) { m_synchronized = true; @@ -1767,7 +1691,6 @@ bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) // If daemon isn't synced a calculated block height will be used instead - //TODO: Handle light wallet scenario where block height = 0. if (isNewWallet() && daemonSynced()) { LOG_PRINT_L2(__FUNCTION__ << ":New Wallet - fast refresh until " << daemonBlockChainHeight()); m_wallet->set_refresh_from_block_height(daemonBlockChainHeight()); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 5756b99f..8fc81120 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -95,7 +95,7 @@ class WalletImpl : public Wallet bool store(const std::string &path); std::string filename() const; std::string keysFilename() const; - bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false); + bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false); bool connectToDaemon(); ConnectionStatus connected() const; void setTrustedDaemon(bool arg); @@ -162,8 +162,6 @@ class WalletImpl : public Wallet virtual void pauseRefresh(); virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error); virtual std::string getDefaultDataDir() const; - virtual bool lightWalletLogin(bool &isNewWallet) const; - virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status); virtual bool blackballOutputs(const std::vector &pubkeys, bool add); virtual bool unblackballOutput(const std::string &pubkey); virtual bool getRing(const std::string &key_image, std::vector &ring) const; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 29055098..c5122065 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -434,10 +434,9 @@ struct Wallet * \param upper_transaction_size_limit * \param daemon_username * \param daemon_password - * \param lightWallet - start wallet in light mode, connect to a openmonero compatible server. * \return - true on success */ - virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false) = 0; + virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false) = 0; /*! * \brief createWatchOnly - Creates a watch only wallet @@ -781,11 +780,6 @@ struct Wallet //! secondary key reuse mitigation virtual void keyReuseMitigation2(bool mitigation) = 0; - //! Light wallet authenticate and login - virtual bool lightWalletLogin(bool &isNewWallet) const = 0; - - //! Initiates a light wallet import wallet request - virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0; }; /** diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e3896a81..65b0518f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -639,14 +639,14 @@ wallet2::wallet2(network_type nettype, bool restricted): m_multisig_rescan_info(NULL), m_multisig_rescan_k(NULL), m_run(true), - m_callback(0), + m_callback(nullptr), m_nettype(nettype), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), - m_refresh_type(RefreshOptimizeCoinbase), + m_refresh_type(RefreshDefault), m_auto_refresh(true), m_refresh_from_block_height(0), m_explicit_refresh_from_block_height(true), @@ -668,12 +668,6 @@ wallet2::wallet2(network_type nettype, bool restricted): m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex), m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR), m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR), - m_light_wallet(false), - m_light_wallet_scanned_block_height(0), - m_light_wallet_blockchain_height(0), - m_light_wallet_connected(false), - m_light_wallet_balance(0), - m_light_wallet_unlocked_balance(0), m_key_on_device(false), m_ring_history_saved(false), m_ringdb() @@ -760,8 +754,6 @@ bool wallet2::init(std::string daemon_address, boost::optional(tx.vout[i].target).key, tx_scan_info.received->derivation, i, tx_scan_info.received->index, tx_scan_info.in_ephemeral, tx_scan_info.ki, m_account.get_device()); - if(!r) - { - LOG_PRINT_L0("Failed to generate key image for tx: " << get_transaction_hash(tx)); - if(tx_scan_info.in_ephemeral.pub != boost::get(tx.vout[i].target).key) - LOG_PRINT_L0("key_image generated ephemeral public key not matched with output_key"); - return; - } + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image for tx: " + epee::string_tools::pod_to_hex(get_transaction_hash(tx))); + THROW_WALLET_EXCEPTION_IF(tx_scan_info.in_ephemeral.pub != boost::get(tx.vout[i].target).key, error::wallet_internal_error, "Key_image generated ephemeral public key not matched with output_key"); } THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice"); - outs.push_back(i); if (tx_scan_info.money_transfered == 0) { tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device()); } + + if(!tx_scan_info.money_transfered) + return; + + outs.push_back(i); tx_money_got_in_outs[tx_scan_info.received->index] += tx_scan_info.money_transfered; tx_scan_info.amount = tx_scan_info.money_transfered; ++num_vouts_received; @@ -1099,7 +1090,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote //ensure device is let in NONE mode in any case hw::device &hwdev = m_account.get_device(); boost::unique_lock hwdev_lock (hwdev); - hw::reset_mode rst(hwdev); + hw::reset_mode rst(hwdev); hwdev_lock.unlock(); // In this function, tx (probably) only contains the base information @@ -1117,28 +1108,37 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote LOG_PRINT_L0("Transaction extra has unsupported format: " << txid); } + tx_extra_pub_key pub_key_field; + bool rp = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field); + if (!rp) + { + MWARNING("Public key wasn't found as primary in extra. Checking additional keys before skipping."); + tx_extra_additional_pub_keys additional_pub_key_field; + bool ra = find_tx_extra_field_by_type(tx_extra_fields, additional_pub_key_field); + if (!ra) + { + MWARNING("Public key wasn't found as additional tx key, or primary tx key. Skipping tx: " << txid); + if(m_callback != nullptr) + m_callback->on_skip_transaction(height, txid, tx); + } + else + { + MERROR("Public key found as additional in tx extra, but not as primary. Not skipping tx: " << txid); + } + } + else + { + tx_pub_key = pub_key_field.pub_key; + } + // Don't try to extract tx public key if tx has no ouputs - size_t pk_index = 0; std::vector tx_scan_info(tx.vout.size()); std::deque output_found(tx.vout.size(), false); uint64_t total_received_1 = 0; - while (!tx.vout.empty()) - { - // if tx.vout is not empty, we loop through all tx pubkeys - - tx_extra_pub_key pub_key_field; - if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) - { - if (pk_index > 1) - break; - LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid); - if(0 != m_callback) - m_callback->on_skip_transaction(height, txid, tx); - break; - } + if (!tx.vout.empty() && (tx_pub_key != null_pkey)) + { int num_vouts_received = 0; - tx_pub_key = pub_key_field.pub_key; tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; const cryptonote::account_keys& keys = m_account.get_keys(); @@ -1156,55 +1156,23 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote // additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx); std::vector additional_derivations; - if (pk_index == 1) + + for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) { - for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) + additional_derivations.push_back({}); + if (!hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back())) { - additional_derivations.push_back({}); - if (!hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back())) - { - MWARNING("Failed to generate key derivation from tx pubkey, skipping"); - additional_derivations.pop_back(); - } + MWARNING("Failed to generate key derivation from tx pubkey, skipping"); + additional_derivations.pop_back(); } } - hwdev_lock.unlock(); + + hwdev_lock.unlock(); if (miner_tx && m_refresh_type == RefreshNoCoinbase) { // assume coinbase isn't for us } - else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) - { - check_acc_out_precomp_once(tx.vout[0], derivation, additional_derivations, 0, tx_scan_info[0], output_found[0]); - THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); - - // this assumes that the miner tx pays a single address - if (tx_scan_info[0].received) - { - // process the other outs from that tx - // the first one was already checked - for (size_t i = 1; i < tx.vout.size(); ++i) - { - tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, - std::ref(tx_scan_info[i]), std::ref(output_found[i]))); - } - waiter.wait(); - // then scan all outputs from 0 - hwdev_lock.lock(); - hwdev.set_mode(hw::device::NONE); - for (size_t i = 0; i < tx.vout.size(); ++i) - { - THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); - if (tx_scan_info[i].received) - { - hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, tx_scan_info[i].received->derivation); - scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs); - } - } - hwdev_lock.unlock(); - } - } else if (tx.vout.size() > 1 && tools::threadpool::getInstance().get_max_concurrency() > 1) { for (size_t i = 0; i < tx.vout.size(); ++i) @@ -1281,7 +1249,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_key_image_known = !m_watch_only && !m_multisig; td.m_key_image_partial = m_multisig; td.m_amount = amount; - td.m_pk_index = pk_index - 1; + td.m_pk_index = 0; td.m_subaddr_index = tx_scan_info[o].received->index; expand_subaddresses(tx_scan_info[o].received->index); if (tx.vout[o].amount == 0) @@ -1311,7 +1279,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info, m_transfers.size() - 1); } LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); - if (0 != m_callback) + if (m_callback != nullptr) m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index); } total_received_1 += amount; @@ -1349,7 +1317,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_tx = (const cryptonote::transaction_prefix&)tx; td.m_txid = txid; td.m_amount = amount; - td.m_pk_index = pk_index - 1; + td.m_pk_index = 0; td.m_subaddr_index = tx_scan_info[o].received->index; expand_subaddresses(tx_scan_info[o].received->index); if (tx.vout[o].amount == 0) @@ -1378,7 +1346,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status"); LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); - if (0 != m_callback) + if (m_callback != nullptr) m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index); } total_received_1 += extra_amount; @@ -1428,7 +1396,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote { LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid); set_spent(it->second, height); - if (0 != m_callback) + if (m_callback != nullptr) m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index); } } @@ -1534,7 +1502,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote payment.m_subaddr_index = i.first; if (pool) { emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen}); - if (0 != m_callback) + if (m_callback != nullptr) m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index); } else @@ -1640,7 +1608,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry m_blockchain.push_back(bl_id); ++m_local_bc_height; - if (0 != m_callback) + if (m_callback != nullptr) m_callback->on_new_block(height, b); } //---------------------------------------------------------------------------------------------------- @@ -1942,7 +1910,7 @@ void wallet2::remove_obsolete_pool_txs(const std::vector &tx_hashe { MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool"); m_unconfirmed_payments.erase(pit); - if (0 != m_callback) + if (m_callback != nullptr) m_callback->on_pool_tx_removed(txid); } } @@ -2215,7 +2183,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, m_blockchain.push_back(bl_id); ++m_local_bc_height; - if (0 != m_callback) + if (m_callback != nullptr) { // FIXME: this isn't right, but simplewallet just logs that we got a block. cryptonote::block dummy; m_callback->on_new_block(current_index, dummy); @@ -2261,39 +2229,6 @@ bool wallet2::delete_address_book_row(std::size_t row_id) { //---------------------------------------------------------------------------------------------------- void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& received_money) { - if(m_light_wallet) { - - // MyMonero get_address_info needs to be called occasionally to trigger wallet sync. - // This call is not really needed for other purposes and can be removed if mymonero changes their backend. - cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response res; - - // Get basic info - if(light_wallet_get_address_info(res)) { - // Last stored block height - uint64_t prev_height = m_light_wallet_blockchain_height; - // Update lw heights - m_light_wallet_scanned_block_height = res.scanned_block_height; - m_light_wallet_blockchain_height = res.blockchain_height; - m_local_bc_height = res.blockchain_height; - // If new height - call new_block callback - if(m_light_wallet_blockchain_height != prev_height) - { - MDEBUG("new block since last time!"); - m_callback->on_lw_new_block(m_light_wallet_blockchain_height - 1); - } - m_light_wallet_connected = true; - MDEBUG("lw scanned block height: " << m_light_wallet_scanned_block_height); - MDEBUG("lw blockchain height: " << m_light_wallet_blockchain_height); - MDEBUG(m_light_wallet_blockchain_height-m_light_wallet_scanned_block_height << " blocks behind"); - // TODO: add wallet created block info - - light_wallet_get_address_txs(); - } else - m_light_wallet_connected = false; - - // Lighwallet refresh done - return; - } received_money = false; blocks_fetched = 0; uint64_t added_blocks = 0; @@ -2848,7 +2783,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_refresh_type = RefreshType::RefreshDefault; if (field_refresh_type_found) { - if (field_refresh_type == RefreshFull || field_refresh_type == RefreshOptimizeCoinbase || field_refresh_type == RefreshNoCoinbase) + if (field_refresh_type == RefreshFull || field_refresh_type == RefreshNoCoinbase) m_refresh_type = (RefreshType)field_refresh_type; else LOG_PRINT_L0("Unknown refresh-type value (" << field_refresh_type << "), using default"); @@ -3761,12 +3696,6 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout) boost::lock_guard lock(m_daemon_rpc_mutex); - // TODO: Add light wallet version check. - if(m_light_wallet) { - version = 0; - return m_light_wallet_connected; - } - if(!m_http_client.is_connected()) { m_node_rpc_proxy.invalidate(); @@ -4086,8 +4015,6 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas uint64_t wallet2::balance(uint32_t index_major) const { uint64_t amount = 0; - if(m_light_wallet) - return m_light_wallet_unlocked_balance; for (const auto& i : balance_per_subaddress(index_major)) amount += i.second; return amount; @@ -4096,8 +4023,6 @@ uint64_t wallet2::balance(uint32_t index_major) const uint64_t wallet2::unlocked_balance(uint32_t index_major) const { uint64_t amount = 0; - if(m_light_wallet) - return m_light_wallet_balance; for (const auto& i : unlocked_balance_per_subaddress(index_major)) amount += i.second; return amount; @@ -4618,22 +4543,6 @@ void wallet2::commit_tx(pending_tx& ptx) { using namespace cryptonote; - if(m_light_wallet) - { - cryptonote::COMMAND_RPC_SUBMIT_RAW_TX::request oreq; - cryptonote::COMMAND_RPC_SUBMIT_RAW_TX::response ores; - oreq.address = get_account().get_public_address_str(m_nettype); - oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - oreq.tx = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/submit_raw_tx", oreq, ores, m_http_client, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx"); - // MyMonero and OpenMonero use different status strings - THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, ores.status, ores.error); - } - else - { // Normal submit COMMAND_RPC_SEND_RAW_TX::request req; req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); @@ -4651,7 +4560,6 @@ void wallet2::commit_tx(pending_tx& ptx) THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Bad output index in selected transfers: " + boost::lexical_cast(idx)); } - } crypto::hash txid; txid = get_transaction_hash(ptx.tx); @@ -5324,8 +5232,6 @@ uint64_t wallet2::get_dynamic_per_kb_fee_estimate() const //---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_per_kb_fee() const { - if(m_light_wallet) - return m_light_wallet_per_kb_fee; return get_dynamic_per_kb_fee_estimate(); } //---------------------------------------------------------------------------------------------------- @@ -5754,120 +5660,12 @@ bool wallet2::tx_add_fake_output(std::vector> &outs, const std::vector &selected_transfers, size_t fake_outputs_count) { - - MDEBUG("LIGHTWALLET - Getting random outs"); - - cryptonote::COMMAND_RPC_GET_RANDOM_OUTS::request oreq; - cryptonote::COMMAND_RPC_GET_RANDOM_OUTS::response ores; - - size_t light_wallet_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1); - - // Amounts to ask for - // MyMonero api handle amounts and fees as strings - for(size_t idx: selected_transfers) { - const uint64_t ask_amount = m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount(); - std::ostringstream amount_ss; - amount_ss << ask_amount; - oreq.amounts.push_back(amount_ss.str()); - } - - oreq.count = light_wallet_requested_outputs_count; - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_random_outs", oreq, ores, m_http_client, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs"); - THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error); - - // Check if we got enough outputs for each amount - for(auto& out: ores.amount_outs) { - const uint64_t out_amount = boost::lexical_cast(out.amount); - THROW_WALLET_EXCEPTION_IF(out.outputs.size() < light_wallet_requested_outputs_count , error::wallet_internal_error, "Not enough outputs for amount: " + boost::lexical_cast(out.amount)); - MDEBUG(out.outputs.size() << " outputs for amount "+ boost::lexical_cast(out.amount) + " received from light wallet node"); - } - - MDEBUG("selected transfers size: " << selected_transfers.size()); - - for(size_t idx: selected_transfers) - { - // Create new index - outs.push_back(std::vector()); - outs.back().reserve(fake_outputs_count + 1); - - // add real output first - const transfer_details &td = m_transfers[idx]; - const uint64_t amount = td.is_rct() ? 0 : td.amount(); - outs.back().push_back(std::make_tuple(td.m_global_output_index, td.get_public_key(), rct::commit(td.amount(), td.m_mask))); - MDEBUG("added real output " << string_tools::pod_to_hex(td.get_public_key())); - - // Even if the lightwallet server returns random outputs, we pick them randomly. - std::vector order; - order.resize(light_wallet_requested_outputs_count); - for (size_t n = 0; n < order.size(); ++n) - order[n] = n; - std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand())); - - - LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs with amounts " << print_money(td.is_rct() ? 0 : td.amount())); - MDEBUG("OUTS SIZE: " << outs.back().size()); - for (size_t o = 0; o < light_wallet_requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o) - { - // Random pick - size_t i = order[o]; - - // Find which random output key to use - bool found_amount = false; - size_t amount_key; - for(amount_key = 0; amount_key < ores.amount_outs.size(); ++amount_key) - { - if(boost::lexical_cast(ores.amount_outs[amount_key].amount) == amount) { - found_amount = true; - break; - } - } - THROW_WALLET_EXCEPTION_IF(!found_amount , error::wallet_internal_error, "Outputs for amount " + boost::lexical_cast(ores.amount_outs[amount_key].amount) + " not found" ); - - LOG_PRINT_L2("Index " << i << "/" << light_wallet_requested_outputs_count << ": idx " << ores.amount_outs[amount_key].outputs[i].global_index << " (real " << td.m_global_output_index << "), unlocked " << "(always in light)" << ", key " << ores.amount_outs[0].outputs[i].public_key); - - // Convert light wallet string data to proper data structures - crypto::public_key tx_public_key; - rct::key mask = AUTO_VAL_INIT(mask); // decrypted mask - not used here - rct::key rct_commit = AUTO_VAL_INIT(rct_commit); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, ores.amount_outs[amount_key].outputs[i].public_key), error::wallet_internal_error, "Invalid public_key"); - string_tools::hex_to_pod(ores.amount_outs[amount_key].outputs[i].public_key, tx_public_key); - const uint64_t global_index = ores.amount_outs[amount_key].outputs[i].global_index; - if(!light_wallet_parse_rct_str(ores.amount_outs[amount_key].outputs[i].rct, tx_public_key, 0, mask, rct_commit, false)) - rct_commit = rct::zeroCommit(td.amount()); - - if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true)) { - MDEBUG("added fake output " << ores.amount_outs[amount_key].outputs[i].public_key); - MDEBUG("index " << global_index); - } - } - - THROW_WALLET_EXCEPTION_IF(outs.back().size() < fake_outputs_count + 1 , error::wallet_internal_error, "Not enough fake outputs found" ); - - // Real output is the first. Shuffle outputs - MTRACE(outs.back().size() << " outputs added. Sorting outputs by index:"); - std::sort(outs.back().begin(), outs.back().end(), [](const get_outs_entry &a, const get_outs_entry &b) { return std::get<0>(a) < std::get<0>(b); }); - - // Print output order - for(auto added_out: outs.back()) - MTRACE(std::get<0>(added_out)); - - } -} void wallet2::get_outs(std::vector> &outs, const std::vector &selected_transfers, size_t fake_outputs_count) { LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); outs.clear(); - if(m_light_wallet && fake_outputs_count > 0) { - light_wallet_get_outs(outs, selected_transfers, fake_outputs_count); - return; - } - crypto::chacha_key key; generate_chacha_key_from_secret_keys(key); @@ -6834,489 +6632,6 @@ static uint32_t get_count_above(const std::vector &tr return count; } -bool wallet2::light_wallet_login(bool &new_address) -{ - MDEBUG("Light wallet login request"); - m_light_wallet_connected = false; - cryptonote::COMMAND_RPC_LOGIN::request request; - cryptonote::COMMAND_RPC_LOGIN::response response; - request.address = get_account().get_public_address_str(m_nettype); - request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - // Always create account if it doesn't exist. - request.create_account = true; - m_daemon_rpc_mutex.lock(); - bool connected = epee::net_utils::invoke_http_json("/login", request, response, m_http_client, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - // MyMonero doesn't send any status message. OpenMonero does. - m_light_wallet_connected = connected && (response.status.empty() || response.status == "success"); - new_address = response.new_address; - MDEBUG("Status: " << response.status); - MDEBUG("Reason: " << response.reason); - MDEBUG("New wallet: " << response.new_address); - if(m_light_wallet_connected) - { - // Clear old data on successful login. - // m_transfers.clear(); - // m_payments.clear(); - // m_unconfirmed_payments.clear(); - } - return m_light_wallet_connected; -} - -bool wallet2::light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response) -{ - MDEBUG("Light wallet import wallet request"); - cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::request oreq; - oreq.address = get_account().get_public_address_str(m_nettype); - oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/import_wallet_request", oreq, response, m_http_client, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "import_wallet_request"); - - - return true; -} - -void wallet2::light_wallet_get_unspent_outs() -{ - MDEBUG("Getting unspent outs"); - - cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::request oreq; - cryptonote::COMMAND_RPC_GET_UNSPENT_OUTS::response ores; - - oreq.amount = "0"; - oreq.address = get_account().get_public_address_str(m_nettype); - oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - // openMonero specific - oreq.dust_threshold = boost::lexical_cast(::config::DEFAULT_DUST_THRESHOLD); - // below are required by openMonero api - but are not used. - oreq.mixin = 0; - oreq.use_dust = true; - - - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_unspent_outs", oreq, ores, m_http_client, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_unspent_outs"); - THROW_WALLET_EXCEPTION_IF(ores.status == "error", error::wallet_internal_error, ores.reason); - - m_light_wallet_per_kb_fee = ores.per_kb_fee; - - std::unordered_map transfers_txs; - for(const auto &t: m_transfers) - transfers_txs.emplace(t.m_txid,t.m_spent); - - MDEBUG("FOUND " << ores.outputs.size() <<" outputs"); - - // return if no outputs found - if(ores.outputs.empty()) - return; - - // Clear old outputs - m_transfers.clear(); - - for (const auto &o: ores.outputs) { - bool spent = false; - bool add_transfer = true; - crypto::key_image unspent_key_image; - crypto::public_key tx_public_key = AUTO_VAL_INIT(tx_public_key); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field"); - string_tools::hex_to_pod(o.tx_pub_key, tx_public_key); - - for (const std::string &ski: o.spend_key_images) { - spent = false; - - // Check if key image is ours - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, ski), error::wallet_internal_error, "Invalid key image"); - string_tools::hex_to_pod(ski, unspent_key_image); - if(light_wallet_key_image_is_ours(unspent_key_image, tx_public_key, o.index)){ - MTRACE("Output " << o.public_key << " is spent. Key image: " << ski); - spent = true; - break; - } { - MTRACE("Unspent output found. " << o.public_key); - } - } - - // Check if tx already exists in m_transfers. - crypto::hash txid; - crypto::public_key tx_pub_key; - crypto::public_key public_key; - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_hash), error::wallet_internal_error, "Invalid tx_hash field"); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.public_key), error::wallet_internal_error, "Invalid public_key field"); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, o.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field"); - string_tools::hex_to_pod(o.tx_hash, txid); - string_tools::hex_to_pod(o.public_key, public_key); - string_tools::hex_to_pod(o.tx_pub_key, tx_pub_key); - - for(auto &t: m_transfers){ - if(t.get_public_key() == public_key) { - t.m_spent = spent; - add_transfer = false; - break; - } - } - - if(!add_transfer) - continue; - - m_transfers.push_back(boost::value_initialized()); - transfer_details& td = m_transfers.back(); - - td.m_block_height = o.height; - td.m_global_output_index = o.global_index; - td.m_txid = txid; - - // Add to extra - add_tx_pub_key_to_extra(td.m_tx, tx_pub_key); - - td.m_key_image = unspent_key_image; - td.m_key_image_known = !m_watch_only && !m_multisig; - td.m_key_image_partial = m_multisig; - td.m_amount = o.amount; - td.m_pk_index = 0; - td.m_internal_output_index = o.index; - td.m_spent = spent; - - tx_out txout; - txout.target = txout_to_key(public_key); - txout.amount = td.m_amount; - - td.m_tx.vout.resize(td.m_internal_output_index + 1); - td.m_tx.vout[td.m_internal_output_index] = txout; - - // Add unlock time and coinbase bool got from get_address_txs api call - std::unordered_map::const_iterator found = m_light_wallet_address_txs.find(txid); - THROW_WALLET_EXCEPTION_IF(found == m_light_wallet_address_txs.end(), error::wallet_internal_error, "Lightwallet: tx not found in m_light_wallet_address_txs"); - bool miner_tx = found->second.m_coinbase; - td.m_tx.unlock_time = found->second.m_unlock_time; - - if (!o.rct.empty()) - { - // Coinbase tx's - if(miner_tx) - { - td.m_mask = rct::identity(); - } - else - { - // rct txs - // decrypt rct mask, calculate commit hash and compare against blockchain commit hash - rct::key rct_commit; - light_wallet_parse_rct_str(o.rct, tx_pub_key, td.m_internal_output_index, td.m_mask, rct_commit, true); - bool valid_commit = (rct_commit == rct::commit(td.amount(), td.m_mask)); - if(!valid_commit) - { - MDEBUG("output index: " << o.global_index); - MDEBUG("mask: " + string_tools::pod_to_hex(td.m_mask)); - MDEBUG("calculated commit: " + string_tools::pod_to_hex(rct::commit(td.amount(), td.m_mask))); - MDEBUG("expected commit: " + string_tools::pod_to_hex(rct_commit)); - MDEBUG("amount: " << td.amount()); - } - THROW_WALLET_EXCEPTION_IF(!valid_commit, error::wallet_internal_error, "Lightwallet: rct commit hash mismatch!"); - } - td.m_rct = true; - } - else - { - td.m_mask = rct::identity(); - td.m_rct = false; - } - if(!spent) - set_unspent(m_transfers.size()-1); - m_key_images[td.m_key_image] = m_transfers.size()-1; - m_pub_keys[td.get_public_key()] = m_transfers.size()-1; - } -} - -bool wallet2::light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response) -{ - MTRACE(__FUNCTION__); - - cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::request request; - - request.address = get_account().get_public_address_str(m_nettype); - request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_address_info", request, response, m_http_client, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_info"); - // TODO: Validate result - return true; -} - -void wallet2::light_wallet_get_address_txs() -{ - MDEBUG("Refreshing light wallet"); - - cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::request ireq; - cryptonote::COMMAND_RPC_GET_ADDRESS_TXS::response ires; - - ireq.address = get_account().get_public_address_str(m_nettype); - ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key); - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_address_txs", ireq, ires, m_http_client, rpc_timeout, "POST"); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_txs"); - //OpenMonero sends status=success, Mymonero doesn't. - THROW_WALLET_EXCEPTION_IF((!ires.status.empty() && ires.status != "success"), error::no_connection_to_daemon, "get_address_txs"); - - - // Abort if no transactions - if(ires.transactions.empty()) - return; - - // Create searchable vectors - std::vector payments_txs; - for(const auto &p: m_payments) - payments_txs.push_back(p.second.m_tx_hash); - std::vector unconfirmed_payments_txs; - for(const auto &up: m_unconfirmed_payments) - unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash); - - // for balance calculation - uint64_t wallet_total_sent = 0; - uint64_t wallet_total_unlocked_sent = 0; - // txs in pool - std::vector pool_txs; - - for (const auto &t: ires.transactions) { - const uint64_t total_received = t.total_received; - uint64_t total_sent = t.total_sent; - - // Check key images - subtract fake outputs from total_sent - for(const auto &so: t.spent_outputs) - { - crypto::public_key tx_public_key; - crypto::key_image key_image; - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.tx_pub_key), error::wallet_internal_error, "Invalid tx_pub_key field"); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, so.key_image), error::wallet_internal_error, "Invalid key_image field"); - string_tools::hex_to_pod(so.tx_pub_key, tx_public_key); - string_tools::hex_to_pod(so.key_image, key_image); - - if(!light_wallet_key_image_is_ours(key_image, tx_public_key, so.out_index)) { - THROW_WALLET_EXCEPTION_IF(so.amount > t.total_sent, error::wallet_internal_error, "Lightwallet: total sent is negative!"); - total_sent -= so.amount; - } - } - - // Do not add tx if empty. - if(total_sent == 0 && total_received == 0) - continue; - - crypto::hash payment_id = null_hash; - crypto::hash tx_hash; - - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, t.payment_id), error::wallet_internal_error, "Invalid payment_id field"); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, t.hash), error::wallet_internal_error, "Invalid hash field"); - string_tools::hex_to_pod(t.payment_id, payment_id); - string_tools::hex_to_pod(t.hash, tx_hash); - - // lightwallet specific info - bool incoming = (total_received > total_sent); - address_tx address_tx; - address_tx.m_tx_hash = tx_hash; - address_tx.m_incoming = incoming; - address_tx.m_amount = incoming ? total_received - total_sent : total_sent - total_received; - address_tx.m_fee = 0; // TODO - address_tx.m_block_height = t.height; - address_tx.m_unlock_time = t.unlock_time; - address_tx.m_timestamp = t.timestamp; - address_tx.m_coinbase = t.coinbase; - address_tx.m_mempool = t.mempool; - m_light_wallet_address_txs.emplace(tx_hash,address_tx); - - // populate data needed for history (m_payments, m_unconfirmed_payments, m_confirmed_txs) - // INCOMING transfers - if(total_received > total_sent) { - payment_details payment; - payment.m_tx_hash = tx_hash; - payment.m_amount = total_received - total_sent; - payment.m_fee = 0; // TODO - payment.m_block_height = t.height; - payment.m_unlock_time = t.unlock_time; - payment.m_timestamp = t.timestamp; - - if (t.mempool) { - if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) { - pool_txs.push_back(tx_hash); - // assume false as we don't get that info from the light wallet server - crypto::hash payment_id; - THROW_WALLET_EXCEPTION_IF(!epee::string_tools::hex_to_pod(t.payment_id, payment_id), - error::wallet_internal_error, "Failed to parse payment id"); - emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, false}); - if (0 != m_callback) { - m_callback->on_lw_unconfirmed_money_received(t.height, payment.m_tx_hash, payment.m_amount); - } - } - } else { - if (std::find(payments_txs.begin(), payments_txs.end(), tx_hash) == payments_txs.end()) { - m_payments.emplace(tx_hash, payment); - if (0 != m_callback) { - m_callback->on_lw_money_received(t.height, payment.m_tx_hash, payment.m_amount); - } - } - } - // Outgoing transfers - } else { - uint64_t amount_sent = total_sent - total_received; - cryptonote::transaction dummy_tx; // not used by light wallet - // increase wallet total sent - wallet_total_sent += total_sent; - if (t.mempool) - { - // Handled by add_unconfirmed_tx in commit_tx - // If sent from another wallet instance we need to add it - if(m_unconfirmed_txs.find(tx_hash) == m_unconfirmed_txs.end()) - { - unconfirmed_transfer_details utd; - utd.m_amount_in = amount_sent; - utd.m_amount_out = amount_sent; - utd.m_change = 0; - utd.m_payment_id = payment_id; - utd.m_timestamp = t.timestamp; - utd.m_state = wallet2::unconfirmed_transfer_details::pending; - m_unconfirmed_txs.emplace(tx_hash,utd); - } - } - else - { - // Only add if new - auto confirmed_tx = m_confirmed_txs.find(tx_hash); - if(confirmed_tx == m_confirmed_txs.end()) { - // tx is added to m_unconfirmed_txs - move to confirmed - if(m_unconfirmed_txs.find(tx_hash) != m_unconfirmed_txs.end()) - { - process_unconfirmed(tx_hash, dummy_tx, t.height); - } - // Tx sent by another wallet instance - else - { - confirmed_transfer_details ctd; - ctd.m_amount_in = amount_sent; - ctd.m_amount_out = amount_sent; - ctd.m_change = 0; - ctd.m_payment_id = payment_id; - ctd.m_block_height = t.height; - ctd.m_timestamp = t.timestamp; - m_confirmed_txs.emplace(tx_hash,ctd); - } - if (0 != m_callback) - { - m_callback->on_lw_money_spent(t.height, tx_hash, amount_sent); - } - } - // If not new - check the amount and update if necessary. - // when sending a tx to same wallet the receiving amount has to be credited - else - { - if(confirmed_tx->second.m_amount_in != amount_sent || confirmed_tx->second.m_amount_out != amount_sent) - { - MDEBUG("Adjusting amount sent/received for tx: <" + t.hash + ">. Is tx sent to own wallet? " << print_money(amount_sent) << " != " << print_money(confirmed_tx->second.m_amount_in)); - confirmed_tx->second.m_amount_in = amount_sent; - confirmed_tx->second.m_amount_out = amount_sent; - confirmed_tx->second.m_change = 0; - } - } - } - } - } - // TODO: purge old unconfirmed_txs - remove_obsolete_pool_txs(pool_txs); - - // Calculate wallet balance - m_light_wallet_balance = ires.total_received-wallet_total_sent; - // MyMonero doesn't send unlocked balance - if(ires.total_received_unlocked > 0) - m_light_wallet_unlocked_balance = ires.total_received_unlocked-wallet_total_sent; - else - m_light_wallet_unlocked_balance = m_light_wallet_balance; -} - -bool wallet2::light_wallet_parse_rct_str(const std::string& rct_string, const crypto::public_key& tx_pub_key, uint64_t internal_output_index, rct::key& decrypted_mask, rct::key& rct_commit, bool decrypt) const -{ - // rct string is empty if output is non RCT - if (rct_string.empty()) - return false; - // rct_string is a string with length 64+64+64 ( + + ) - rct::key encrypted_mask; - std::string rct_commit_str = rct_string.substr(0,64); - std::string encrypted_mask_str = rct_string.substr(64,64); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, rct_commit_str), error::wallet_internal_error, "Invalid rct commit hash: " + rct_commit_str); - THROW_WALLET_EXCEPTION_IF(string_tools::validate_hex(64, encrypted_mask_str), error::wallet_internal_error, "Invalid rct mask: " + encrypted_mask_str); - string_tools::hex_to_pod(rct_commit_str, rct_commit); - string_tools::hex_to_pod(encrypted_mask_str, encrypted_mask); - if (decrypt) { - // Decrypt the mask - crypto::key_derivation derivation; - bool r = generate_key_derivation(tx_pub_key, get_account().get_keys().m_view_secret_key, derivation); - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); - crypto::secret_key scalar; - crypto::derivation_to_scalar(derivation, internal_output_index, scalar); - sc_sub(decrypted_mask.bytes,encrypted_mask.bytes,rct::hash_to_scalar(rct::sk2rct(scalar)).bytes); - } - return true; -} - -bool wallet2::light_wallet_key_image_is_ours(const crypto::key_image& key_image, const crypto::public_key& tx_public_key, uint64_t out_index) -{ - // Lookup key image from cache - std::map index_keyimage_map; - std::unordered_map >::const_iterator found_pub_key = m_key_image_cache.find(tx_public_key); - if(found_pub_key != m_key_image_cache.end()) { - // pub key found. key image for index cached? - index_keyimage_map = found_pub_key->second; - std::map::const_iterator index_found = index_keyimage_map.find(out_index); - if(index_found != index_keyimage_map.end()) - return key_image == index_found->second; - } - - // Not in cache - calculate key image - crypto::key_image calculated_key_image; - cryptonote::keypair in_ephemeral; - - // Subaddresses aren't supported in mymonero/openmonero yet. Roll out the original scheme: - // compute D = a*R - // compute P = Hs(D || i)*G + B - // compute x = Hs(D || i) + b (and check if P==x*G) - // compute I = x*Hp(P) - const account_keys& ack = get_account().get_keys(); - crypto::key_derivation derivation; - bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, derivation); - CHECK_AND_ASSERT_MES(r, false, "failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); - - r = crypto::derive_public_key(derivation, out_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub); - CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key (" << derivation << ", " << out_index << ", " << ack.m_account_address.m_spend_public_key << ")"); - - crypto::derive_secret_key(derivation, out_index, ack.m_spend_secret_key, in_ephemeral.sec); - crypto::public_key out_pkey_test; - r = crypto::secret_key_to_public_key(in_ephemeral.sec, out_pkey_test); - CHECK_AND_ASSERT_MES(r, false, "failed to secret_key_to_public_key(" << in_ephemeral.sec << ")"); - CHECK_AND_ASSERT_MES(in_ephemeral.pub == out_pkey_test, false, "derived secret key doesn't match derived public key"); - - crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, calculated_key_image); - - index_keyimage_map.emplace(out_index, calculated_key_image); - m_key_image_cache.emplace(tx_public_key, index_keyimage_map); - return key_image == calculated_key_image; -} - -// Another implementation of transaction creation that is hopefully better -// While there is anything left to pay, it goes through random outputs and tries -// to fill the next destination/amount. If it fully fills it, it will use the -// remainder to try to fill the next one as well. -// The tx size if roughly estimated as a linear function of only inputs, and a -// new tx will be created when that size goes above a given fraction of the -// max tx size. At that point, more outputs may be added if the fee cannot be -// satisfied. -// If the next output in the next tx would go to the same destination (ie, we -// cut off at a tx boundary in the middle of paying a given destination), the -// fee will be carved out of the current input if possible, to avoid having to -// add another output just for the fee and getting change. -// This system allows for sending (almost) the entire balance, since it does -// not generate spurious change in all txes, thus decreasing the instantaneous -// usable balance. std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, bool trusted_daemon) { //ensure device is let in NONE mode in any case @@ -7324,10 +6639,6 @@ std::vector wallet2::create_transactions_2(std::vector hwdev_lock (hwdev); hw::reset_mode rst(hwdev); - if(m_light_wallet) { - // Populate m_transfers - light_wallet_get_unspent_outs(); - } std::vector>> unused_transfers_indices_per_subaddr; std::vector>> unused_dust_indices_per_subaddr; uint64_t needed_money; @@ -8071,8 +7382,6 @@ void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) con bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const { // TODO: How to get fork rule info from light wallet node? - if(m_light_wallet) - return true; uint64_t height, earliest_height; boost::optional result = m_node_rpc_proxy.get_height(height); throw_on_rpc_response_error(result, "get_info"); @@ -9262,57 +8571,11 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle { // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key } - - // Due to a previous bug, there might be more than one tx pubkey in extra, one being - // the result of a previously discarded signature. - // For speed, since scanning for outputs is a slow process, we check whether extra - // contains more than one pubkey. If not, the first one is returned. If yes, they're - // checked for whether they yield at least one output tx_extra_pub_key pub_key_field; - THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 0), error::wallet_internal_error, - "Public key wasn't found in the transaction extra"); - const crypto::public_key tx_pub_key = pub_key_field.pub_key; - bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1); - if (!two_found) { - // easy case, just one found - return tx_pub_key; - } - - // more than one, loop and search - const cryptonote::account_keys& keys = m_account.get_keys(); - size_t pk_index = 0; - hw::device &hwdev = m_account.get_device(); - - const std::vector additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); - std::vector additional_derivations; - for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) - { - additional_derivations.push_back({}); - bool r = hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()); - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); - } - - while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) { - const crypto::public_key tx_pub_key = pub_key_field.pub_key; - crypto::key_derivation derivation; - bool r = hwdev.generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation); - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); - - for (size_t i = 0; i < td.m_tx.vout.size(); ++i) - { - tx_scan_info_t tx_scan_info; - check_acc_out_precomp(td.m_tx.vout[i], derivation, additional_derivations, i, tx_scan_info); - if (!tx_scan_info.error && tx_scan_info.received) - return tx_pub_key; - } - } - - // we found no key yielding an output - THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, - "Public key yielding at least one output wasn't found in the transaction extra"); - return crypto::null_pkey; + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field), error::wallet_internal_error, "Public key wasn't found in the transaction extra"); + return pub_key_field.pub_key; } - +//--------------------------------------------------------------------------------------------------- bool wallet2::export_key_images(const std::string &filename) const { std::vector> ski = export_key_images(); @@ -9432,8 +8695,8 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent ski.push_back(std::make_pair(key_image, signature)); } - - return import_key_images(ski, spent, unspent); + + return import_key_images(ski, spent, unspent); } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 7e59bb5a..bf445be5 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -142,9 +142,8 @@ namespace tools enum RefreshType { RefreshFull, - RefreshOptimizeCoinbase, RefreshNoCoinbase, - RefreshDefault = RefreshOptimizeCoinbase, + RefreshDefault = RefreshFull, }; static const char* tr(const char* str); @@ -603,14 +602,6 @@ namespace tools bool is_deterministic() const; bool get_seed(std::string& electrum_words, const epee::wipeable_string &passphrase = epee::wipeable_string()) const; - /*! - * \brief Checks if light wallet. A light wallet sends view key to a server where the blockchain is scanned. - */ - bool light_wallet() const { return m_light_wallet; } - void set_light_wallet(bool light_wallet) { m_light_wallet = light_wallet; } - uint64_t get_light_wallet_scanned_block_height() const { return m_light_wallet_scanned_block_height; } - uint64_t get_light_wallet_blockchain_height() const { return m_light_wallet_blockchain_height; } - /*! * \brief Gets the seed language */ @@ -1001,24 +992,6 @@ namespace tools uint64_t adjust_mixin(uint64_t mixin) const; uint32_t adjust_priority(uint32_t priority); - // Light wallet specific functions - // fetch unspent outs from lw node and store in m_transfers - void light_wallet_get_unspent_outs(); - // fetch txs and store in m_payments - void light_wallet_get_address_txs(); - // get_address_info - bool light_wallet_get_address_info(cryptonote::COMMAND_RPC_GET_ADDRESS_INFO::response &response); - // Login. new_address is true if address hasn't been used on lw node before. - bool light_wallet_login(bool &new_address); - // Send an import request to lw node. returns info about import fee, address and payment_id - bool light_wallet_import_wallet_request(cryptonote::COMMAND_RPC_IMPORT_WALLET_REQUEST::response &response); - // get random outputs from light wallet server - void light_wallet_get_outs(std::vector> &outs, const std::vector &selected_transfers, size_t fake_outputs_count); - // Parse rct string - bool light_wallet_parse_rct_str(const std::string& rct_string, const crypto::public_key& tx_pub_key, uint64_t internal_output_index, rct::key& decrypted_mask, rct::key& rct_commit, bool decrypt) const; - // check if key image is ours - bool light_wallet_key_image_is_ours(const crypto::key_image& key_image, const crypto::public_key& tx_public_key, uint64_t out_index); - /* * "attributes" are a mechanism to store an arbitrary number of string values * on the level of the wallet as a whole, identified by keys. Their introduction, @@ -1212,17 +1185,6 @@ namespace tools std::unordered_set m_scanned_pool_txs[2]; size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor; - // Light wallet - bool m_light_wallet; /* sends view key to daemon for scanning */ - uint64_t m_light_wallet_scanned_block_height; - uint64_t m_light_wallet_blockchain_height; - uint64_t m_light_wallet_per_kb_fee = DYNAMIC_FEE_PER_KB_BASE_FEE; - bool m_light_wallet_connected; - uint64_t m_light_wallet_balance; - uint64_t m_light_wallet_unlocked_balance; - // Light wallet info needed to populate m_payment requires 2 separate api calls (get_address_txs and get_unspent_outs) - // We save the info from the first call in m_light_wallet_address_txs for easier lookup. - std::unordered_map m_light_wallet_address_txs; // store calculated key image for faster lookup std::unordered_map > m_key_image_cache; @@ -1853,7 +1815,7 @@ namespace tools return true; }); THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx); - + bool dust_sent_elsewhere = (dust_policy.addr_for_dust.m_view_public_key != change_dts.addr.m_view_public_key || dust_policy.addr_for_dust.m_spend_public_key != change_dts.addr.m_spend_public_key);