aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/node_rpc_proxy.cpp30
-rw-r--r--src/wallet/node_rpc_proxy.h1
-rw-r--r--src/wallet/wallet2.cpp193
-rw-r--r--src/wallet/wallet2.h3
-rw-r--r--src/wallet/wallet_rpc_server.cpp4
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 &current_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());
}