diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/node_rpc_proxy.cpp | 30 | ||||
-rw-r--r-- | src/wallet/node_rpc_proxy.h | 1 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 193 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 3 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 4 |
5 files changed, 137 insertions, 94 deletions
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 44c52df62..aa0b4dfd3 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -392,6 +392,36 @@ boost::optional<std::string> NodeRPCProxy::get_rpc_payment_info(bool mining, boo return boost::none; } +boost::optional<std::string> NodeRPCProxy::get_transactions(const std::vector<crypto::hash> &txids, const std::function<void(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request&, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response&, bool)> &f) +{ + const size_t SLICE_SIZE = 100; // RESTRICTED_TRANSACTIONS_COUNT as defined in rpc/core_rpc_server.cpp + for (size_t offset = 0; offset < txids.size(); offset += SLICE_SIZE) + { + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req_t = AUTO_VAL_INIT(req_t); + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response resp_t = AUTO_VAL_INIT(resp_t); + + const size_t n_txids = std::min<size_t>(SLICE_SIZE, txids.size() - offset); + for (size_t n = offset; n < (offset + n_txids); ++n) + req_t.txs_hashes.push_back(epee::string_tools::pod_to_hex(txids[n])); + MDEBUG("asking for " << req_t.txs_hashes.size() << " transactions"); + req_t.decode_as_json = false; + req_t.prune = true; + + bool r = false; + { + const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; + uint64_t pre_call_credits = m_rpc_payment_state.credits; + req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key); + r = net_utils::invoke_http_json("/gettransactions", req_t, resp_t, m_http_client, rpc_timeout); + if (r && resp_t.status == CORE_RPC_STATUS_OK) + check_rpc_cost(m_rpc_payment_state, "/gettransactions", resp_t.credits, pre_call_credits, resp_t.txs.size() * COST_PER_TX); + } + + f(req_t, resp_t, r); + } + return boost::optional<std::string>(); +} + boost::optional<std::string> NodeRPCProxy::get_block_header_by_height(uint64_t height, cryptonote::block_header_response &block_header) { if (m_offline) diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index f78cd6427..0088090a8 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -59,6 +59,7 @@ public: boost::optional<std::string> get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_blocks, std::vector<uint64_t> &fees); boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask); boost::optional<std::string> get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie); + boost::optional<std::string> get_transactions(const std::vector<crypto::hash> &txids, const std::function<void(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request&, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response&, bool)> &f); boost::optional<std::string> get_block_header_by_height(uint64_t height, cryptonote::block_header_response &block_header); private: diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 76d7b2a4f..1ea699ef1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1350,6 +1350,7 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u m_rpc_payment_state.discrepancy = 0; m_rpc_version = 0; m_node_rpc_proxy.invalidate(); + m_pool_info_query_time = 0; } const std::string address = get_daemon_address(); @@ -2940,6 +2941,82 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl error = !cryptonote::parse_and_validate_block_from_blob(blob, bl, bl_id); } //---------------------------------------------------------------------------------------------------- +void read_pool_txs(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request &req, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response &res, bool r, const std::vector<crypto::hash> &txids, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs) +{ + if (r && res.status == CORE_RPC_STATUS_OK) + { + MDEBUG("Reading pool txs"); + if (res.txs.size() == req.txs_hashes.size()) + { + for (const auto &tx_entry: res.txs) + { + if (tx_entry.in_pool) + { + cryptonote::transaction tx; + cryptonote::blobdata bd; + crypto::hash tx_hash; + + if (get_pruned_tx(tx_entry, tx, tx_hash)) + { + const std::vector<crypto::hash>::const_iterator i = std::find_if(txids.begin(), txids.end(), + [tx_hash](const crypto::hash &e) { return e == tx_hash; }); + if (i != txids.end()) + { + txs.push_back(std::make_tuple(tx, tx_hash, tx_entry.double_spend_seen)); + } + else + { + MERROR("Got txid " << tx_hash << " which we did not ask for"); + } + } + else + { + LOG_PRINT_L0("Failed to parse transaction from daemon"); + } + } + else + { + LOG_PRINT_L1("Transaction from daemon was in pool, but is no more"); + } + } + } + else + { + LOG_PRINT_L0("Expected " << req.txs_hashes.size() << " out of " << txids.size() << " tx(es), got " << res.txs.size()); + } + } +} +//---------------------------------------------------------------------------------------------------- +void wallet2::process_pool_info_extent(const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response &res, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed) +{ + std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> added_pool_txs; + added_pool_txs.reserve(res.added_pool_txs.size() + res.remaining_added_pool_txids.size()); + + for (const auto &pool_tx: res.added_pool_txs) + { + cryptonote::transaction tx; + THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_base_from_blob(pool_tx.tx_blob, tx), + error::wallet_internal_error, "Failed to validate transaction base from daemon"); + added_pool_txs.push_back(std::make_tuple(tx, pool_tx.tx_hash, pool_tx.double_spend_seen)); + } + + // getblocks.bin may return more added pool transactions than we're allowed to request in restricted mode + if (!res.remaining_added_pool_txids.empty()) + { + // request the remaining txs + m_node_rpc_proxy.get_transactions(res.remaining_added_pool_txids, + [this, &res, &added_pool_txs](const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request &req_t, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response &resp_t, bool r) + { + read_pool_txs(req_t, resp_t, r, res.remaining_added_pool_txids, added_pool_txs); + if (!r || resp_t.status != CORE_RPC_STATUS_OK) + LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(resp_t.status)); + } + ); + } + + update_pool_state_from_pool_data(res.pool_info_extent == COMMAND_RPC_GET_BLOCKS_FAST::INCREMENTAL, res.removed_pool_txids, added_pool_txs, process_txs, refreshed); +} +//---------------------------------------------------------------------------------------------------- void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t ¤t_height) { cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); @@ -2965,7 +3042,7 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error, "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" + boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon"); - uint64_t pool_info_cost = res.added_pool_txs.size() * COST_PER_TX + res.removed_pool_txids.size() * COST_PER_POOL_HASH; + uint64_t pool_info_cost = res.added_pool_txs.size() * COST_PER_TX + (res.remaining_added_pool_txids.size() + res.removed_pool_txids.size()) * COST_PER_POOL_HASH; check_rpc_cost("/getblocks.bin", res.credits, pre_call_credits, 1 + res.blocks.size() * COST_PER_BLOCK + pool_info_cost); } @@ -2984,7 +3061,7 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh { if (res.pool_info_extent != COMMAND_RPC_GET_BLOCKS_FAST::NONE) { - update_pool_state_from_pool_data(res, m_process_pool_txs, true); + process_pool_info_extent(res, m_process_pool_txs, true); } else { @@ -3510,14 +3587,14 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction, req.client = get_client_signature(); bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, *m_http_client, rpc_timeout); THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status)); - uint64_t pool_info_cost = res.added_pool_txs.size() * COST_PER_TX + res.removed_pool_txids.size() * COST_PER_POOL_HASH; + uint64_t pool_info_cost = res.added_pool_txs.size() * COST_PER_TX + (res.remaining_added_pool_txids.size() + res.removed_pool_txids.size()) * COST_PER_POOL_HASH; check_rpc_cost("/getblocks.bin", res.credits, pre_call_credits, pool_info_cost); } m_pool_info_query_time = res.daemon_time; if (res.pool_info_extent != COMMAND_RPC_GET_BLOCKS_FAST::NONE) { - update_pool_state_from_pool_data(res, process_txs, refreshed); + process_pool_info_extent(res, process_txs, refreshed); updated = true; } // We SHOULD get pool data here, but if for some crazy reason we don't fall back to the "old" method @@ -3587,85 +3664,22 @@ void wallet2::update_pool_state_by_pool_query(std::vector<std::tuple<cryptonote: MTRACE("update_pool_state_by_pool_query done second loop"); // gather txids of new pool txes to us - std::vector<std::pair<crypto::hash, bool>> txids; + std::vector<crypto::hash> txids; for (const auto &txid: res.tx_hashes) { if (accept_pool_tx_for_processing(txid)) - txids.push_back({txid, false}); + txids.push_back(txid); } - // get_transaction_pool_hashes.bin may return more transactions than we're allowed to request in restricted mode - const size_t SLICE_SIZE = 100; // RESTRICTED_TRANSACTIONS_COUNT as defined in rpc/core_rpc_server.cpp - for (size_t offset = 0; offset < txids.size(); offset += SLICE_SIZE) - { - cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; - cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res; - - const size_t n_txids = std::min<size_t>(SLICE_SIZE, txids.size() - offset); - for (size_t n = offset; n < (offset + n_txids); ++n) { - req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txids.at(n).first)); - } - MDEBUG("asking for " << req.txs_hashes.size() << " transactions"); - req.decode_as_json = false; - req.prune = true; - - bool r; + m_node_rpc_proxy.get_transactions(txids, + [this, &txids, &process_txs](const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request &req_t, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response &resp_t, bool r) { - const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; - uint64_t pre_call_credits = m_rpc_payment_state.credits; - req.client = get_client_signature(); - r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout); - if (r && res.status == CORE_RPC_STATUS_OK) - check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX); + read_pool_txs(req_t, resp_t, r, txids, process_txs); + if (!r || resp_t.status != CORE_RPC_STATUS_OK) + LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(resp_t.status)); } + ); - MDEBUG("Got " << r << " and " << res.status); - if (r && res.status == CORE_RPC_STATUS_OK) - { - if (res.txs.size() == req.txs_hashes.size()) - { - for (const auto &tx_entry: res.txs) - { - if (tx_entry.in_pool) - { - cryptonote::transaction tx; - cryptonote::blobdata bd; - crypto::hash tx_hash; - - if (get_pruned_tx(tx_entry, tx, tx_hash)) - { - const std::vector<std::pair<crypto::hash, bool>>::const_iterator i = std::find_if(txids.begin(), txids.end(), - [tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; }); - if (i != txids.end()) - { - process_txs.push_back(std::make_tuple(tx, tx_hash, tx_entry.double_spend_seen)); - } - else - { - MERROR("Got txid " << tx_hash << " which we did not ask for"); - } - } - else - { - LOG_PRINT_L0("Failed to parse transaction from daemon"); - } - } - else - { - LOG_PRINT_L1("Transaction from daemon was in pool, but is no more"); - } - } - } - else - { - LOG_PRINT_L0("Expected " << n_txids << " out of " << txids.size() << " tx(es), got " << res.txs.size()); - } - } - else - { - LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(res.status)); - } - } MTRACE("update_pool_state_by_pool_query end"); } //---------------------------------------------------------------------------------------------------- @@ -3673,15 +3687,13 @@ void wallet2::update_pool_state_by_pool_query(std::vector<std::tuple<cryptonote: // txs that are new in the pool since the last time we queried and the ids of txs that were // removed from the pool since then, or the whole content of the pool if incremental was not // possible, e.g. because the server was just started or restarted. -void wallet2::update_pool_state_from_pool_data(const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response &res, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed) +void wallet2::update_pool_state_from_pool_data(bool incremental, const std::vector<crypto::hash> &removed_pool_txids, const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &added_pool_txs, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed) { MTRACE("update_pool_state_from_pool_data start"); auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() { m_encrypt_keys_after_refresh.reset(); }); - bool incremental = res.pool_info_extent == COMMAND_RPC_GET_BLOCKS_FAST::INCREMENTAL; - if (refreshed) { if (incremental) @@ -3690,7 +3702,7 @@ void wallet2::update_pool_state_from_pool_data(const cryptonote::COMMAND_RPC_GET // pool; do so only after refresh to not delete too early and too eagerly; maybe we will find the tx // later in a block, or not, or find it again in the pool txs because it was first removed but then // somehow quickly "resurrected" - that all does not matter here, we retrace the removal - remove_obsolete_pool_txs(res.removed_pool_txids, true); + remove_obsolete_pool_txs(removed_pool_txids, true); } else { @@ -3698,10 +3710,10 @@ void wallet2::update_pool_state_from_pool_data(const cryptonote::COMMAND_RPC_GET // unfortunate that we have to build a new vector with ids first, but better than copying and // modifying the code of 'remove_obsolete_pool_txs' here std::vector<crypto::hash> txids; - txids.reserve(res.added_pool_txs.size()); - for (const auto &it: res.added_pool_txs) + txids.reserve(added_pool_txs.size()); + for (const auto &pool_tx: added_pool_txs) { - txids.push_back(it.tx_hash); + txids.push_back(std::get<1>(pool_tx)); } remove_obsolete_pool_txs(txids, false); } @@ -3715,9 +3727,9 @@ void wallet2::update_pool_state_from_pool_data(const cryptonote::COMMAND_RPC_GET const crypto::hash &txid = it->first; MDEBUG("Checking m_unconfirmed_txs entry " << txid); bool found = false; - for (const auto &it2: res.added_pool_txs) + for (const auto &pool_tx: added_pool_txs) { - if (it2.tx_hash == txid) + if (std::get<1>(pool_tx) == txid) { found = true; break; @@ -3732,16 +3744,11 @@ void wallet2::update_pool_state_from_pool_data(const cryptonote::COMMAND_RPC_GET // if we work incrementally and thus see only new pool txs since last time we asked it should // be rare that we know already about one of those, but check nevertheless process_txs.clear(); - for (const auto &pool_tx: res.added_pool_txs) + for (const auto &pool_tx: added_pool_txs) { - cryptonote::transaction tx; - THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(pool_tx.tx_blob, tx), - error::wallet_internal_error, "Failed to validate transaction from daemon"); - const crypto::hash &txid = pool_tx.tx_hash; - bool take = accept_pool_tx_for_processing(txid); - if (take) + if (accept_pool_tx_for_processing(std::get<1>(pool_tx))) { - process_txs.push_back(std::make_tuple(tx, txid, pool_tx.double_spend_seen)); + process_txs.push_back(pool_tx); } } @@ -4323,6 +4330,7 @@ bool wallet2::clear() m_subaddress_labels.clear(); m_multisig_rounds_passed = 0; m_device_last_key_image_sync = 0; + m_pool_info_query_time = 0; m_skip_to_height = 0; return true; } @@ -4340,6 +4348,7 @@ void wallet2::clear_soft(bool keep_key_images) m_unconfirmed_payments.clear(); m_scanned_pool_txs[0].clear(); m_scanned_pool_txs[1].clear(); + m_pool_info_query_time = 0; m_skip_to_height = 0; cryptonote::block b; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 589a077ab..f07e1c41a 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1740,8 +1740,9 @@ private: void process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); bool accept_pool_tx_for_processing(const crypto::hash &txid); void process_unconfirmed_transfer(bool incremental, const crypto::hash &txid, wallet2::unconfirmed_transfer_details &tx_details, bool seen_in_pool, std::chrono::system_clock::time_point now, bool refreshed); + void process_pool_info_extent(const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response &res, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed); void update_pool_state_by_pool_query(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false); - void update_pool_state_from_pool_data(const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response &res, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed); + void update_pool_state_from_pool_data(bool incremental, const std::vector<crypto::hash> &removed_pool_txids, const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &added_pool_txs, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed); uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const; bool prepare_file_names(const std::string& file_path); void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 32628d65b..4f1b3bd21 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -149,8 +149,10 @@ namespace tools return true; if (boost::posix_time::microsec_clock::universal_time() < m_last_auto_refresh_time + boost::posix_time::seconds(m_auto_refresh_period)) return true; + uint64_t blocks_fetched = 0; try { - if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon()); + bool received_money = false; + if (m_wallet) m_wallet->refresh(m_wallet->is_trusted_daemon(), 0, blocks_fetched, received_money, true, true); } catch (const std::exception& ex) { LOG_ERROR("Exception at while refreshing, what=" << ex.what()); } |